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] == 49) */
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);
225 static void print_client_stats(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 *, 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 && !sleep_time)) {
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)
520 print_server_stats(opt_prt);
523 print_client_stats(opt_prt);
527 print_server_stats(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 int has_v2_stats = has_stats(srvproc2info, SRVPROC2_SZ+2);
586 int has_v3_stats = has_stats(srvproc3info, SRVPROC3_SZ+2);
587 int has_v4_stats = has_stats(srvproc4info, SRVPROC4_SZ+2);
589 if ((opt_prt & PRNT_V2) ||
590 ((opt_prt & PRNT_AUTO) && has_v2_stats)) {
591 if (!opt_sleep || has_v2_stats) {
592 print_callstats(LABEL_srvproc2,
593 nfsv2name, srvproc2info + 1,
594 sizeof(nfsv2name)/sizeof(char *));
597 if ((opt_prt & PRNT_V3) ||
598 ((opt_prt & PRNT_AUTO) && has_v3_stats)) {
599 if (!opt_sleep || has_v3_stats) {
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_v4_stats)) {
607 if (!opt_sleep || has_v4_stats) {
608 print_callstats( LABEL_srvproc4,
609 nfssrvproc4name, srvproc4info + 1,
610 sizeof(nfssrvproc4name)/sizeof(char *));
611 print_callstats(LABEL_srvproc4ops,
612 nfssrvproc4opname, srvproc4opsinfo + 1,
613 sizeof(nfssrvproc4opname)/sizeof(char *));
619 print_client_stats(int opt_prt)
621 if (opt_prt & PRNT_NET) {
622 if (opt_sleep && !has_rpcstats(cltnetinfo, 4)) {
625 print_numbers(LABEL_cltnet
626 "packets udp tcp tcpconn\n",
631 if (opt_prt & PRNT_RPC) {
632 if (opt_sleep && !has_rpcstats(cltrpcinfo, 3)) {
635 print_numbers(LABEL_cltrpc
636 "calls retrans authrefrsh\n",
641 if (opt_prt & PRNT_CALLS) {
642 int has_v2_stats = has_stats(cltproc2info, CLTPROC2_SZ+2);
643 int has_v3_stats = has_stats(cltproc3info, CLTPROC3_SZ+2);
644 int has_v4_stats = has_stats(cltproc4info, CLTPROC4_SZ+2);
645 if ((opt_prt & PRNT_V2) ||
646 ((opt_prt & PRNT_AUTO) && has_v2_stats)) {
647 if (!opt_sleep || has_v2_stats) {
648 print_callstats(LABEL_cltproc2,
649 nfsv2name, cltproc2info + 1,
650 sizeof(nfsv2name)/sizeof(char *));
653 if ((opt_prt & PRNT_V3) ||
654 ((opt_prt & PRNT_AUTO) && has_v3_stats)) {
655 if (!opt_sleep || has_v3_stats) {
656 print_callstats(LABEL_cltproc3,
657 nfsv3name, cltproc3info + 1,
658 sizeof(nfsv3name)/sizeof(char *));
661 if ((opt_prt & PRNT_V4) ||
662 ((opt_prt & PRNT_AUTO) && has_v4_stats)) {
663 if (!opt_sleep || has_v4_stats) {
664 print_callstats(LABEL_cltproc4,
665 nfscltproc4name, cltproc4info + 1,
666 sizeof(nfscltproc4name)/sizeof(char *));
673 print_clnt_list(int opt_prt)
675 if (opt_prt & PRNT_CALLS) {
676 int has_v2_stats = has_stats(cltproc2info, CLTPROC2_SZ+2);
677 int has_v3_stats = has_stats(cltproc3info, CLTPROC3_SZ+2);
678 int has_v4_stats = has_stats(cltproc4info, CLTPROC4_SZ+2);
679 if ((opt_prt & PRNT_V2) ||
680 ((opt_prt & PRNT_AUTO) && has_v2_stats)) {
681 if (!opt_sleep || has_v2_stats) {
682 print_callstats_list("nfs v2 client",
683 nfsv2name, cltproc2info + 1,
684 sizeof(nfsv2name)/sizeof(char *));
687 if ((opt_prt & PRNT_V3) ||
688 ((opt_prt & PRNT_AUTO) && has_v3_stats)) {
689 if (!opt_sleep || has_v3_stats) {
690 print_callstats_list("nfs v3 client",
691 nfsv3name, cltproc3info + 1,
692 sizeof(nfsv3name)/sizeof(char *));
695 if ((opt_prt & PRNT_V4) ||
696 ((opt_prt & PRNT_AUTO) && has_v4_stats)) {
697 if (!opt_sleep || has_v4_stats) {
698 print_callstats_list("nfs v4 client",
699 nfscltproc4name, cltproc4info + 1,
700 sizeof(nfscltproc4name)/sizeof(char *));
706 print_serv_list(int opt_prt)
708 if (opt_prt & PRNT_CALLS) {
709 int has_v2_stats = has_stats(srvproc2info, SRVPROC2_SZ+2);
710 int has_v3_stats = has_stats(srvproc3info, SRVPROC3_SZ+2);
711 int has_v4_stats = has_stats(srvproc4info, SRVPROC4_SZ+2);
712 if ((opt_prt & PRNT_V2) ||
713 ((opt_prt & PRNT_AUTO) && has_v2_stats)) {
714 if (!opt_sleep || has_v2_stats) {
715 print_callstats_list("nfs v2 server",
716 nfsv2name, srvproc2info + 1,
717 sizeof(nfsv2name)/sizeof(char *));
720 if ((opt_prt & PRNT_V3) ||
721 ((opt_prt & PRNT_AUTO) && has_v3_stats)) {
722 if (!opt_sleep || has_v3_stats) {
723 print_callstats_list("nfs v3 server",
724 nfsv3name, srvproc3info + 1,
725 sizeof(nfsv3name)/sizeof(char *));
728 if ((opt_prt & PRNT_V4) ||
729 ((opt_prt & PRNT_AUTO) && has_v4_stats)) {
730 if (!opt_sleep || has_v4_stats) {
731 print_callstats_list("nfs v4 server",
732 nfssrvproc4name, srvproc4info + 1,
733 sizeof(nfssrvproc4name)/sizeof(char *));
734 print_callstats_list("nfs v4 servop",
735 nfssrvproc4opname, srvproc4opsinfo + 1,
736 sizeof(nfssrvproc4opname)/sizeof(char *));
742 print_stats_list(int opt_srv, int opt_clt, int opt_prt)
745 print_serv_list(opt_prt);
748 print_clnt_list(opt_prt);
752 get_stat_info(const char *sp, struct statinfo *statp)
756 for (ip = statp; ip->tag; ip++) {
757 if (!strcmp(sp, ip->tag))
765 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
770 for (i = 0; i < nr; i++)
771 printf("%s%-8u", i? " " : "", info[i]);
776 print_callstats(const char *hdr, const char **names,
777 unsigned int *info, unsigned int nr)
779 unsigned long long total;
780 unsigned long long pct;
784 for (i = 0, total = 0; i < nr; i++)
788 for (i = 0; i < nr; i += 6) {
789 for (j = 0; j < 6 && i + j < nr; j++)
790 printf("%-13s", names[i+j]);
792 for (j = 0; j < 6 && i + j < nr; j++) {
793 pct = ((unsigned long long) info[i+j]*100)/total;
794 printf("%-8u%3llu%% ", info[i+j], pct);
802 print_callstats_list(const char *hdr, const char **names,
803 unsigned int *callinfo, unsigned int nr)
805 unsigned long long calltotal;
808 for (i = 0, calltotal = 0; i < nr; i++) {
809 calltotal += callinfo[i];
813 printf("%13s %13s %8llu \n", hdr, "total:", calltotal);
814 printf("------------- ------------- --------\n");
815 for (i = 0; i < nr; i++) {
817 printf("%13s %12s: %8u \n", hdr, names[i], callinfo[i]);
824 /* returns 0 on success, 1 otherwise */
826 parse_raw_statfile(const char *name, struct statinfo *statp)
828 char buffer[4096], *next;
831 /* Being unable to read e.g. the nfsd stats file shouldn't
832 * be a fatal error -- it usually means the module isn't loaded.
834 if ((fp = fopen(name, "r")) == NULL) {
835 // fprintf(stderr, "Warning: %s: %m\n", name);
839 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
841 char *sp, *line = buffer;
843 unsigned int total = 0;
845 if ((next = strchr(line, '\n')) != NULL)
847 if (!(sp = strtok(line, " \t")))
850 ip = get_stat_info(sp, statp);
856 for (i = 0; i < cnt; i++) {
857 if (!(sp = strtok(NULL, " \t")))
859 ip->valptr[i] = (unsigned int) strtoul(sp, NULL, 0);
860 total += ip->valptr[i];
862 ip->valptr[cnt - 1] = total;
869 /* returns 0 on success, 1 otherwise */
871 parse_pretty_statfile(const char *filename, struct statinfo *info)
873 int numvals, curindex, numconsumed, n, err = 1;
875 char buf[4096], *bufp, *fmt, is_proc;
879 if ((fp = fopen(filename, "r")) == NULL)
880 //err(2, "Unable to open statfile '%s'.\n", filename);
883 while (fgets(buf, sizeof(buf), fp) != NULL) {
884 for (ip = info; ip->tag; ip++) {
885 if (strcmp(buf, ip->label))
889 numvals = ip->nrvals - 1;
890 is_proc = strncmp("proc", ip->tag, 4) ? 0 : 1;
892 fmt = " %u %*u%% %n";
900 /* get (and skip) header */
901 if (fgets(buf, sizeof(buf), fp) == NULL) {
902 fprintf(stderr, "Failed to locate header after "
903 "label for '%s' in %s.\n",
907 /* no header -- done with this "tag" */
909 ip->valptr[numvals] = sum;
913 if (fgets(buf, sizeof(buf), fp) == NULL) {
914 fprintf(stderr, "Failed to locate stats after "
915 "header for '%s' in %s.\n",
920 for (; curindex < numvals; curindex++) {
921 n = sscanf(bufp, fmt, &ip->valptr[curindex],
929 sum += ip->valptr[curindex];
943 mounts(const char *name)
945 char buffer[4096], *next;
948 /* Being unable to read e.g. the nfsd stats file shouldn't
949 * be a fatal error -- it usually means the module isn't loaded.
951 if ((fp = fopen(name, "r")) == NULL) {
952 fprintf(stderr, "Warning: %s: %m\n", name);
956 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
958 char *device, *mount, *type, *flags;
960 if ((next = strchr(line, '\n')) != NULL)
963 if (!(device = strtok(line, " \t")))
966 if (!(mount = strtok(NULL, " \t")))
969 if (!(type = strtok(NULL, " \t")))
972 if (strcmp(type, "nfs") && strcmp(type,"nfs4")) {
976 if (!(flags = strtok(NULL, " \t")))
979 printf("%s from %s\n", mount, device);
980 printf(" Flags:\t%s\n", flags);
991 get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
997 char *label = is_srv ? "Server" : "Client";
999 /* try to guess what type of stat file we're dealing with */
1000 if ((fp = fopen(file, "r")) == NULL)
1002 if (fgets(buf, 10, fp) == NULL)
1004 if (!strncmp(buf, "net ", 4)) {
1005 /* looks like raw client stats */
1007 fprintf(stderr, "Warning: no server info present in "
1008 "raw client stats file.\n");
1011 err = parse_raw_statfile(file, info);
1012 } else if (!strncmp(buf, "rc ", 3)) {
1013 /* looks like raw server stats */
1015 fprintf(stderr, "Warning: no client info present in "
1016 "raw server stats file.\n");
1019 err = parse_raw_statfile(file, info);
1021 /* looks like pretty client and server stats */
1022 err = parse_pretty_statfile(file, info);
1028 fprintf(stderr, "Error: No %s Stats (%s: %m). \n",
1037 * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
1038 * denotes the number of subsequent entries. statinfo value arrays contain an additional
1039 * field at the end which contains the sum of all previous elements in the array -- so,
1040 * there are stats if the sum's greater than the entry-count.
1043 has_stats(const unsigned int *info, int nr)
1045 return (info[0] && info[nr-1] > info[0]);
1048 has_rpcstats(const unsigned int *info, int size)
1052 for (i=0, cnt=0; i < size; i++)
1058 * take the difference of each individual stat value in 'new' and 'old'
1059 * and store the results back into 'new'
1062 diff_stats(struct statinfo *new, struct statinfo *old, int is_srv)
1064 int i, j, nodiff_first_index, should_diff;
1067 * Different stat types have different formats in the /proc
1068 * files: for the proc2/3/4-type stats, the first entry has
1069 * the total number of subsequent entries; one does not want
1070 * to diff that first entry. The other stat types aren't like
1071 * this. So, we diff a given entry if it's not of one of the
1072 * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
1073 * the first entry ("j" > 0).
1075 nodiff_first_index = 2 + (2 * is_srv);
1077 for (i = 0; old[i].tag; i++) {
1078 for (j = 0; j < new[i].nrvals; j++) {
1079 should_diff = (i < nodiff_first_index || j > 0);
1081 new[i].valptr[j] -= old[i].valptr[j];
1085 * Make sure that the "totals" entry (last value in
1086 * each stat array) for the procX-type stats has the
1087 * "numentries" entry's (first value in procX-type
1088 * stat arrays) constant value added-back after the
1089 * diff -- i.e., it should always be included in the
1092 if (!strncmp("proc", new[i].tag, 4) && old[i].valptr[0])
1093 new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
1101 int minutes, seconds;
1104 endtime = time(NULL);
1105 time_diff = difftime(endtime, starttime);
1106 minutes = time_diff / 60;
1107 seconds = (int)time_diff % 60;
1108 printf("Signal %d received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", sig, minutes, seconds);
1112 update_old_counters(struct statinfo *new, struct statinfo *old)
1115 for (z = 0; old[z].tag; z++)
1116 for (i = 0; i <= old[z].nrvals; i++)
1117 old[z].valptr[i] += new[z].valptr[i];