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",
122 /* nfsv4.1 client ops */
134 /* nfsv4.1 pnfs client ops to data server only */
139 static const char * nfssrvproc4opname[SRVPROC4OPS_SZ] = {
140 "op0-unused", "op1-unused", "op2-future", "access", "close", "commit",
141 "create", "delegpurge", "delegreturn", "getattr", "getfh", "link",
142 "lock", "lockt", "locku", "lookup", "lookup_root", "nverify",
143 "open", "openattr", "open_conf", "open_dgrd", "putfh", "putpubfh",
144 "putrootfh", "read", "readdir", "readlink", "remove", "rename",
145 "renew", "restorefh", "savefh", "secinfo", "setattr", "setcltid",
146 "setcltidconf", "verify", "write", "rellockowner",
147 /* nfsv4.1 server ops */
169 #define LABEL_srvnet "Server packet stats:\n"
170 #define LABEL_srvrpc "Server rpc stats:\n"
171 #define LABEL_srvrc "Server reply cache:\n"
172 #define LABEL_srvfh "Server file handle cache:\n"
173 #define LABEL_srvproc2 "Server nfs v2:\n"
174 #define LABEL_srvproc3 "Server nfs v3:\n"
175 #define LABEL_srvproc4 "Server nfs v4:\n"
176 #define LABEL_srvproc4ops "Server nfs v4 operations:\n"
177 #define LABEL_cltnet "Client packet stats:\n"
178 #define LABEL_cltrpc "Client rpc stats:\n"
179 #define LABEL_cltproc2 "Client nfs v2:\n"
180 #define LABEL_cltproc3 "Client nfs v3:\n"
181 #define LABEL_cltproc4 "Client nfs v4:\n"
183 typedef struct statinfo {
187 unsigned int * valptr;
191 * We now build the arrays of statinfos using macros, which will make it easier
192 * to add new variables for --sleep. e.g., SRV(net) expands into the struct
193 * statinfo: { "net", "Server packet stats:\n", 5, srvnetinfo }
195 #define ARRAYSIZE(x) sizeof(x)/sizeof(*x)
196 #define STATINFO(k, t, s...) { #t, LABEL_##k##t, ARRAYSIZE(k##t##info##s), k##t##info##s }
197 #define SRV(t, s...) STATINFO(srv, t, s)
198 #define CLT(t, s...) STATINFO(clt, t, s)
199 #define DECLARE_SRV(n, s...) static statinfo n##s[] = { \
208 { NULL, NULL, 0, NULL }\
210 #define DECLARE_CLT(n, s...) static statinfo n##s[] = { \
216 { NULL, NULL, 0, NULL }\
218 DECLARE_SRV(srvinfo);
219 DECLARE_SRV(srvinfo, _old);
220 DECLARE_CLT(cltinfo);
221 DECLARE_CLT(cltinfo, _old);
223 static void print_all_stats(int, int, int);
224 static void print_server_stats(int, int);
225 static void print_client_stats(int, int);
226 static void print_stats_list(int, int, int);
227 static void print_numbers(const char *, unsigned int *,
229 static void print_callstats(const char *, const char **,
230 unsigned int *, unsigned int);
231 static void print_callstats_list(const char *, const char **,
232 unsigned int *, unsigned int);
233 static int parse_raw_statfile(const char *, struct statinfo *);
234 static int parse_pretty_statfile(const char *, struct statinfo *);
236 static statinfo *get_stat_info(const char *, struct statinfo *);
238 static int mounts(const char *);
240 static void get_stats(const char *, struct statinfo *, int *, int,
242 static int has_stats(const unsigned int *);
243 static int has_rpcstats(const unsigned int *, int);
244 static void diff_stats(struct statinfo *, struct statinfo *, int);
245 static void unpause(int);
246 static void update_old_counters(struct statinfo *, struct statinfo *);
248 static time_t starttime;
250 #define PRNT_CALLS 0x0001
251 #define PRNT_RPC 0x0002
252 #define PRNT_NET 0x0004
253 #define PRNT_FH 0x0008
254 #define PRNT_RC 0x0010
255 #define PRNT_AUTO 0x1000
256 #define PRNT_V2 0x2000
257 #define PRNT_V3 0x4000
258 #define PRNT_V4 0x8000
259 #define PRNT_ALL 0x0fff
267 void usage(char *name)
269 printf("Usage: %s [OPTION]...\n\
271 -m, --mounts Show statistics on mounted NFS filesystems\n\
272 -c, --client Show NFS client statistics\n\
273 -s, --server Show NFS server statistics\n\
274 -2 Show NFS version 2 statistics\n\
275 -3 Show NFS version 3 statistics\n\
276 -4 Show NFS version 4 statistics\n\
277 -o [facility] Show statistics on particular facilities.\n\
278 nfs NFS protocol information\n\
279 rpc General RPC information\n\
280 net Network layer statistics\n\
281 fh Usage information on the server's file handle cache\n\
282 rc Usage information on the server's request reply cache\n\
283 all Select all of the above\n\
284 -v, --verbose, --all Same as '-o all'\n\
285 -r, --rpc Show RPC statistics\n\
286 -n, --nfs Show NFS statistics\n\
287 -Z[#], --sleep[=#] Collects stats until interrupted.\n\
288 Cumulative stats are then printed\n\
289 If # is provided, stats will be output every\n\
291 -S, --since file Shows difference between current stats and those in 'file'\n\
292 -l, --list Prints stats in list format\n\
293 --version Show program version\n\
294 --help What you just did\n\
299 static struct option longopts[] =
301 { "acl", 0, 0, 'a' },
302 { "all", 0, 0, 'v' },
303 { "auto", 0, 0, '\3' },
304 { "client", 0, 0, 'c' },
305 { "mounted", 0, 0, 'm' },
306 { "nfs", 0, 0, 'n' },
307 { "rpc", 0, 0, 'r' },
308 { "server", 0, 0, 's' },
309 { "verbose", 0, 0, 'v' },
310 { "zero", 0, 0, 'z' },
311 { "help", 0, 0, '\1' },
312 { "version", 0, 0, '\2' },
313 { "sleep", 2, 0, 'Z' },
314 { "since", 1, 0, 'S' },
315 { "list", 0, 0, 'l' },
321 main(int argc, char **argv)
332 *serverfile = NFSSRVSTAT,
333 *clientfile = NFSCLTSTAT;
335 struct statinfo *serverinfo = srvinfo,
336 *serverinfo_tmp = srvinfo_old,
337 *clientinfo = cltinfo,
338 *clientinfo_tmp = cltinfo_old;
340 struct sigaction act = {
341 .sa_handler = unpause,
342 .sa_flags = SA_ONESHOT,
345 if ((progname = strrchr(argv[0], '/')))
350 while ((c = getopt_long(argc, argv, "234acmno:Z::S:vrslz\1\2", longopts, NULL)) != EOF) {
353 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
359 opt_prt |= PRNT_CALLS;
362 if (!strcmp(optarg, "nfs"))
363 opt_prt |= PRNT_CALLS;
364 else if (!strcmp(optarg, "rpc"))
366 else if (!strcmp(optarg, "net"))
368 else if (!strcmp(optarg, "rc"))
370 else if (!strcmp(optarg, "fh"))
372 else if (!strcmp(optarg, "all"))
373 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
375 fprintf(stderr, "nfsstat: unknown category: "
383 sleep_time = atoi(optarg);
394 opt_prt |= versions[c - '2'];
400 opt_prt |= PRNT_AUTO;
412 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
413 "not yet supported\n");
416 return mounts(MOUNTSFILE);
421 fprintf(stdout, "nfsstat: " VERSION "\n");
424 printf("Try `%s --help' for more information.\n", progname);
430 opt_srv = opt_clt = 1;
433 if (!(opt_srv + opt_clt))
434 opt_srv = opt_clt = 1;
435 if (!(opt_prt & 0xfff)) {
436 opt_prt |= PRNT_CALLS + PRNT_RPC;
438 if (!(opt_prt & 0xe000)) {
439 opt_prt |= PRNT_AUTO;
441 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
443 "You requested file handle or request cache "
444 "statistics while using the -c option.\n"
445 "This information is available only for the NFS "
449 if (opt_since || opt_sleep) {
450 serverinfo = srvinfo_old;
451 serverinfo_tmp = srvinfo;
452 clientinfo = cltinfo_old;
453 clientinfo_tmp = cltinfo;
457 get_stats(serverfile, serverinfo, &opt_srv, opt_clt, 1);
459 get_stats(clientfile, clientinfo, &opt_clt, opt_srv, 0);
461 if (opt_sleep && !sleep_time) {
462 starttime = time(NULL);
463 printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
464 if (sigaction(SIGINT, &act, NULL) != 0) {
465 fprintf(stderr, "Error: couldn't register for signal and pause.\n");
471 if (opt_since || opt_sleep) {
473 get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, 1);
474 diff_stats(serverinfo_tmp, serverinfo, 1);
477 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
478 diff_stats(clientinfo_tmp, clientinfo, 0);
484 get_stats(NFSSRVSTAT, serverinfo_tmp , &opt_srv, opt_clt, 1);
485 diff_stats(serverinfo_tmp, serverinfo, 1);
488 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
489 diff_stats(clientinfo_tmp, clientinfo, 0);
492 print_stats_list(opt_srv, opt_clt, opt_prt);
494 print_all_stats(opt_srv, opt_clt, opt_prt);
499 update_old_counters(serverinfo_tmp, serverinfo);
501 update_old_counters(clientinfo_tmp, clientinfo);
507 print_stats_list(opt_srv, opt_clt, opt_prt);
509 print_all_stats(opt_srv, opt_clt, opt_prt);
517 print_all_stats (int opt_srv, int opt_clt, int opt_prt)
519 print_server_stats(opt_srv, opt_prt);
520 print_client_stats(opt_clt, opt_prt);
524 print_server_stats(int opt_srv, int opt_prt)
529 if (opt_prt & PRNT_NET) {
530 if (opt_sleep && !has_rpcstats(srvnetinfo, 4)) {
532 print_numbers( LABEL_srvnet
533 "packets udp tcp tcpconn\n",
538 if (opt_prt & PRNT_RPC) {
539 if (opt_sleep && !has_rpcstats(srvrpcinfo, 5)) {
542 print_numbers(LABEL_srvrpc
543 "calls badcalls badauth badclnt xdrcall\n",
548 if (opt_prt & PRNT_RC) {
549 if (opt_sleep && !has_rpcstats(srvrcinfo, 3)) {
552 print_numbers(LABEL_srvrc
553 "hits misses nocache\n",
560 * 2.2 puts all fh-related info after the 'rc' header
561 * 2.4 puts all fh-related info after the 'fh' header, but relocates
562 * 'stale' to the start and swaps dir and nondir :-(
563 * We preseve the 2.2 order
565 if (opt_prt & PRNT_FH) {
566 if (get_stat_info("fh", srvinfo)) { /* >= 2.4 */
567 int t = srvfhinfo[3];
568 srvfhinfo[3]=srvfhinfo[4];
571 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
575 "lookup anon ncachedir ncachedir stale\n",
580 "lookup anon ncachedir ncachedir stale\n",
584 if (opt_prt & PRNT_CALLS) {
585 if ((opt_prt & PRNT_V2) ||
586 ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
587 if (opt_sleep && !has_stats(srvproc2info)) {
590 print_callstats(LABEL_srvproc2,
591 nfsv2name, srvproc2info + 1,
592 sizeof(nfsv2name)/sizeof(char *));
595 if ((opt_prt & PRNT_V3) ||
596 ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
597 if (opt_sleep && !has_stats(srvproc3info)) {
600 print_callstats(LABEL_srvproc3,
601 nfsv3name, srvproc3info + 1,
602 sizeof(nfsv3name)/sizeof(char *));
605 if ((opt_prt & PRNT_V4) ||
606 ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
607 if (opt_sleep && !has_stats(srvproc4info)) {
610 print_callstats( LABEL_srvproc4,
611 nfssrvproc4name, srvproc4info + 1,
612 sizeof(nfssrvproc4name)/sizeof(char *));
613 print_callstats(LABEL_srvproc4ops,
614 nfssrvproc4opname, srvproc4opsinfo + 1,
615 sizeof(nfssrvproc4opname)/sizeof(char *));
621 print_client_stats(int opt_clt, int opt_prt)
626 if (opt_prt & PRNT_NET) {
627 if (opt_sleep && !has_rpcstats(cltnetinfo, 4)) {
630 print_numbers(LABEL_cltnet
631 "packets udp tcp tcpconn\n",
636 if (opt_prt & PRNT_RPC) {
637 if (opt_sleep && !has_rpcstats(cltrpcinfo, 3)) {
640 print_numbers(LABEL_cltrpc
641 "calls retrans authrefrsh\n",
646 if (opt_prt & PRNT_CALLS) {
647 if ((opt_prt & PRNT_V2) ||
648 ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
649 if (opt_sleep && !has_stats(cltproc2info)) {
652 print_callstats(LABEL_cltproc2,
653 nfsv2name, cltproc2info + 1,
654 sizeof(nfsv2name)/sizeof(char *));
657 if ((opt_prt & PRNT_V3) ||
658 ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
659 if (opt_sleep && !has_stats(cltproc3info)) {
662 print_callstats(LABEL_cltproc3,
663 nfsv3name, cltproc3info + 1,
664 sizeof(nfsv3name)/sizeof(char *));
667 if ((opt_prt & PRNT_V4) ||
668 ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
669 if (opt_sleep && !has_stats(cltproc4info)) {
672 print_callstats(LABEL_cltproc4,
673 nfscltproc4name, cltproc4info + 1,
674 sizeof(nfscltproc4name)/sizeof(char *));
681 print_clnt_list(int opt_prt)
683 if (opt_prt & PRNT_CALLS) {
684 if ((opt_prt & PRNT_V2) ||
685 ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
686 if (opt_sleep && !has_stats(cltproc2info)) {
689 print_callstats_list("nfs v2 client",
690 nfsv2name, cltproc2info + 1,
691 sizeof(nfsv2name)/sizeof(char *));
694 if ((opt_prt & PRNT_V3) ||
695 ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
696 if (opt_sleep && !has_stats(cltproc3info)) {
699 print_callstats_list("nfs v3 client",
700 nfsv3name, cltproc3info + 1,
701 sizeof(nfsv3name)/sizeof(char *));
704 if ((opt_prt & PRNT_V4) ||
705 ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
706 if (opt_sleep && !has_stats(cltproc4info)) {
709 print_callstats_list("nfs v4 ops",
710 nfssrvproc4opname, srvproc4opsinfo + 1,
711 sizeof(nfssrvproc4opname)/sizeof(char *));
712 print_callstats_list("nfs v4 client",
713 nfscltproc4name, cltproc4info + 1,
714 sizeof(nfscltproc4name)/sizeof(char *));
720 print_serv_list(int opt_prt)
722 if (opt_prt & PRNT_CALLS) {
723 if ((opt_prt & PRNT_V2) ||
724 ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
725 if (opt_sleep && !has_stats(srvproc2info)) {
728 print_callstats_list("nfs v2 server",
729 nfsv2name, srvproc2info + 1,
730 sizeof(nfsv2name)/sizeof(char *));
733 if ((opt_prt & PRNT_V3) ||
734 ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
735 if (opt_sleep && !has_stats(srvproc3info)) {
738 print_callstats_list("nfs v3 server",
739 nfsv3name, srvproc3info + 1,
740 sizeof(nfsv3name)/sizeof(char *));
743 if ((opt_prt & PRNT_V4) ||
744 ((opt_prt & PRNT_AUTO) && has_stats(srvproc4opsinfo))) {
745 if (opt_sleep && !has_stats(srvproc4info)) {
748 print_callstats_list("nfs v4 ops",
749 nfssrvproc4opname, srvproc4opsinfo + 1,
750 sizeof(nfssrvproc4opname)/sizeof(char *));
756 print_stats_list(int opt_srv, int opt_clt, int opt_prt)
759 print_serv_list(opt_prt);
762 print_clnt_list(opt_prt);
766 get_stat_info(const char *sp, struct statinfo *statp)
770 for (ip = statp; ip->tag; ip++) {
771 if (!strcmp(sp, ip->tag))
779 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
784 for (i = 0; i < nr; i++)
785 printf("%s%-8u", i? " " : "", info[i]);
790 print_callstats(const char *hdr, const char **names,
791 unsigned int *info, unsigned int nr)
793 unsigned long long total;
794 unsigned long long pct;
798 for (i = 0, total = 0; i < nr; i++)
802 for (i = 0; i < nr; i += 6) {
803 for (j = 0; j < 6 && i + j < nr; j++)
804 printf("%-13s", names[i+j]);
806 for (j = 0; j < 6 && i + j < nr; j++) {
807 pct = ((unsigned long long) info[i+j]*100)/total;
808 printf("%-8u%3llu%% ", info[i+j], pct);
816 print_callstats_list(const char *hdr, const char **names,
817 unsigned int *callinfo, unsigned int nr)
819 unsigned long long calltotal;
822 for (i = 0, calltotal = 0; i < nr; i++) {
823 calltotal += callinfo[i];
827 printf("%13s %13s %8llu \n", hdr, "total:", calltotal);
828 printf("------------- ------------- --------\n");
829 for (i = 0; i < nr; i++) {
831 printf("%13s %12s: %8u \n", hdr, names[i], callinfo[i]);
838 /* returns 0 on success, 1 otherwise */
840 parse_raw_statfile(const char *name, struct statinfo *statp)
842 char buffer[4096], *next;
845 /* Being unable to read e.g. the nfsd stats file shouldn't
846 * be a fatal error -- it usually means the module isn't loaded.
848 if ((fp = fopen(name, "r")) == NULL) {
849 // fprintf(stderr, "Warning: %s: %m\n", name);
853 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
855 char *sp, *line = buffer;
857 unsigned int total = 0;
859 if ((next = strchr(line, '\n')) != NULL)
861 if (!(sp = strtok(line, " \t")))
864 ip = get_stat_info(sp, statp);
870 for (i = 0; i < cnt; i++) {
871 if (!(sp = strtok(NULL, " \t")))
873 ip->valptr[i] = (unsigned int) strtoul(sp, NULL, 0);
874 total += ip->valptr[i];
876 ip->valptr[cnt - 1] = total;
883 /* returns 0 on success, 1 otherwise */
885 parse_pretty_statfile(const char *filename, struct statinfo *info)
887 int numvals, curindex, numconsumed, n, err = 1;
889 char buf[4096], *bufp, *fmt, is_proc;
893 if ((fp = fopen(filename, "r")) == NULL)
894 //err(2, "Unable to open statfile '%s'.\n", filename);
897 while (fgets(buf, sizeof(buf), fp) != NULL) {
898 for (ip = info; ip->tag; ip++) {
899 if (strcmp(buf, ip->label))
903 numvals = ip->nrvals - 1;
904 is_proc = strncmp("proc", ip->tag, 4) ? 0 : 1;
906 fmt = " %u %*u%% %n";
914 /* get (and skip) header */
915 if (fgets(buf, sizeof(buf), fp) == NULL) {
916 fprintf(stderr, "Failed to locate header after "
917 "label for '%s' in %s.\n",
921 /* no header -- done with this "tag" */
923 ip->valptr[numvals] = sum;
927 if (fgets(buf, sizeof(buf), fp) == NULL) {
928 fprintf(stderr, "Failed to locate stats after "
929 "header for '%s' in %s.\n",
934 for (; curindex < numvals; curindex++) {
935 n = sscanf(bufp, fmt, &ip->valptr[curindex],
943 sum += ip->valptr[curindex];
957 mounts(const char *name)
959 char buffer[4096], *next;
962 /* Being unable to read e.g. the nfsd stats file shouldn't
963 * be a fatal error -- it usually means the module isn't loaded.
965 if ((fp = fopen(name, "r")) == NULL) {
966 fprintf(stderr, "Warning: %s: %m\n", name);
970 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
972 char *device, *mount, *type, *flags;
974 if ((next = strchr(line, '\n')) != NULL)
977 if (!(device = strtok(line, " \t")))
980 if (!(mount = strtok(NULL, " \t")))
983 if (!(type = strtok(NULL, " \t")))
986 if (strcmp(type, "nfs") && strcmp(type,"nfs4")) {
990 if (!(flags = strtok(NULL, " \t")))
993 printf("%s from %s\n", mount, device);
994 printf(" Flags:\t%s\n", flags);
1005 get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
1011 char *label = is_srv ? "Server" : "Client";
1013 /* try to guess what type of stat file we're dealing with */
1014 if ((fp = fopen(file, "r")) == NULL)
1016 if (fgets(buf, 10, fp) == NULL)
1018 if (!strncmp(buf, "net ", 4)) {
1019 /* looks like raw client stats */
1021 fprintf(stderr, "Warning: no server info present in "
1022 "raw client stats file.\n");
1025 err = parse_raw_statfile(file, info);
1026 } else if (!strncmp(buf, "rc ", 3)) {
1027 /* looks like raw server stats */
1029 fprintf(stderr, "Warning: no client info present in "
1030 "raw server stats file.\n");
1033 err = parse_raw_statfile(file, info);
1035 /* looks like pretty client and server stats */
1036 err = parse_pretty_statfile(file, info);
1042 fprintf(stderr, "Error: No %s Stats (%s: %m). \n",
1051 * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
1052 * denotes the number of subsequent entries. statinfo value arrays contain an additional
1053 * field at the end which contains the sum of all previous elements in the array -- so,
1054 * there are stats if the sum's greater than the entry-count.
1057 has_stats(const unsigned int *info)
1059 return (info[0] && info[info[0] + 1] > info[0]);
1062 has_rpcstats(const unsigned int *info, int size)
1066 for (i=0, cnt=0; i < size; i++)
1072 * take the difference of each individual stat value in 'new' and 'old'
1073 * and store the results back into 'new'
1076 diff_stats(struct statinfo *new, struct statinfo *old, int is_srv)
1078 int i, j, nodiff_first_index, should_diff;
1081 * Different stat types have different formats in the /proc
1082 * files: for the proc2/3/4-type stats, the first entry has
1083 * the total number of subsequent entries; one does not want
1084 * to diff that first entry. The other stat types aren't like
1085 * this. So, we diff a given entry if it's not of one of the
1086 * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
1087 * the first entry ("j" > 0).
1089 nodiff_first_index = 2 + (2 * is_srv);
1091 for (i = 0; old[i].tag; i++) {
1092 for (j = 0; j < new[i].nrvals; j++) {
1093 should_diff = (i < nodiff_first_index || j > 0);
1095 new[i].valptr[j] -= old[i].valptr[j];
1099 * Make sure that the "totals" entry (last value in
1100 * each stat array) for the procX-type stats has the
1101 * "numentries" entry's (first value in procX-type
1102 * stat arrays) constant value added-back after the
1103 * diff -- i.e., it should always be included in the
1106 if (!strncmp("proc", new[i].tag, 4) && old[i].valptr[0])
1107 new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
1115 int minutes, seconds;
1118 endtime = time(NULL);
1119 time_diff = difftime(endtime, starttime);
1120 minutes = time_diff / 60;
1121 seconds = (int)time_diff % 60;
1122 printf("Signal %d received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", sig, minutes, seconds);
1126 update_old_counters(struct statinfo *new, struct statinfo *old)
1129 for (z = 0; old[z].tag; z++)
1130 for (i = 0; i <= old[z].nrvals; i++)
1131 old[z].valptr[i] += new[z].valptr[i];