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[49], cltproc4info_old[49]; /* NFSv4 call counts ([0] == 35) */
34 static unsigned int srvproc4opsinfo[61], srvproc4opsinfo_old[61]; /* 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[47] = {
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",
104 /* nfsv4.1 client ops */
115 /* nfsv4.1 pnfs client ops to data server only */
120 static const char * nfssrvproc4opname[59] = {
121 "op0-unused", "op1-unused", "op2-future", "access", "close", "commit",
122 "create", "delegpurge", "delegreturn", "getattr", "getfh", "link",
123 "lock", "lockt", "locku", "lookup", "lookup_root", "nverify",
124 "open", "openattr", "open_conf", "open_dgrd", "putfh", "putpubfh",
125 "putrootfh", "read", "readdir", "readlink", "remove", "rename",
126 "renew", "restorefh", "savefh", "secinfo", "setattr", "setcltid",
127 "setcltidconf", "verify", "write", "rellockowner",
128 /* nfsv4.1 server ops */
150 #define LABEL_srvnet "Server packet stats:\n"
151 #define LABEL_srvrpc "Server rpc stats:\n"
152 #define LABEL_srvrc "Server reply cache:\n"
153 #define LABEL_srvfh "Server file handle cache:\n"
154 #define LABEL_srvproc2 "Server nfs v2:\n"
155 #define LABEL_srvproc3 "Server nfs v3:\n"
156 #define LABEL_srvproc4 "Server nfs v4:\n"
157 #define LABEL_srvproc4ops "Server nfs v4 operations:\n"
158 #define LABEL_cltnet "Client packet stats:\n"
159 #define LABEL_cltrpc "Client rpc stats:\n"
160 #define LABEL_cltproc2 "Client nfs v2:\n"
161 #define LABEL_cltproc3 "Client nfs v3:\n"
162 #define LABEL_cltproc4 "Client nfs v4:\n"
164 typedef struct statinfo {
168 unsigned int * valptr;
172 * We now build the arrays of statinfos using macros, which will make it easier
173 * to add new variables for --sleep. e.g., SRV(net) expands into the struct
174 * statinfo: { "net", "Server packet stats:\n", 5, srvnetinfo }
176 #define ARRAYSIZE(x) sizeof(x)/sizeof(*x)
177 #define STATINFO(k, t, s...) { #t, LABEL_##k##t, ARRAYSIZE(k##t##info##s), k##t##info##s }
178 #define SRV(t, s...) STATINFO(srv, t, s)
179 #define CLT(t, s...) STATINFO(clt, t, s)
180 #define DECLARE_SRV(n, s...) static statinfo n##s[] = { \
189 { NULL, NULL, 0, NULL }\
191 #define DECLARE_CLT(n, s...) static statinfo n##s[] = { \
197 { NULL, NULL, 0, NULL }\
199 DECLARE_SRV(srvinfo);
200 DECLARE_SRV(srvinfo, _old);
201 DECLARE_CLT(cltinfo);
202 DECLARE_CLT(cltinfo, _old);
204 static void print_all_stats(int, int, int);
205 static void print_server_stats(int, int);
206 static void print_client_stats(int, int);
207 static void print_stats_list(int, int, int);
208 static void print_numbers(const char *, unsigned int *,
210 static void print_callstats(const char *, const char **,
211 unsigned int *, unsigned int);
212 static void print_callstats_list(const char *, const char **,
213 unsigned int *, unsigned int);
214 static int parse_raw_statfile(const char *, struct statinfo *);
215 static int parse_pretty_statfile(const char *, struct statinfo *);
217 static statinfo *get_stat_info(const char *, struct statinfo *);
219 static int mounts(const char *);
221 static void get_stats(const char *, struct statinfo *, int *, int,
223 static int has_stats(const unsigned int *);
224 static int has_rpcstats(const unsigned int *, int);
225 static void diff_stats(struct statinfo *, struct statinfo *, int);
226 static void unpause(int);
227 static void update_old_counters(struct statinfo *, struct statinfo *);
229 static time_t starttime;
231 #define PRNT_CALLS 0x0001
232 #define PRNT_RPC 0x0002
233 #define PRNT_NET 0x0004
234 #define PRNT_FH 0x0008
235 #define PRNT_RC 0x0010
236 #define PRNT_AUTO 0x1000
237 #define PRNT_V2 0x2000
238 #define PRNT_V3 0x4000
239 #define PRNT_V4 0x8000
240 #define PRNT_ALL 0x0fff
248 void usage(char *name)
250 printf("Usage: %s [OPTION]...\n\
252 -m, --mounts Show statistics on mounted NFS filesystems\n\
253 -c, --client Show NFS client statistics\n\
254 -s, --server Show NFS server statistics\n\
255 -2 Show NFS version 2 statistics\n\
256 -3 Show NFS version 3 statistics\n\
257 -4 Show NFS version 4 statistics\n\
258 -o [facility] Show statistics on particular facilities.\n\
259 nfs NFS protocol information\n\
260 rpc General RPC information\n\
261 net Network layer statistics\n\
262 fh Usage information on the server's file handle cache\n\
263 rc Usage information on the server's request reply cache\n\
264 all Select all of the above\n\
265 -v, --verbose, --all Same as '-o all'\n\
266 -r, --rpc Show RPC statistics\n\
267 -n, --nfs Show NFS statistics\n\
268 -Z[#], --sleep[=#] Collects stats until interrupted.\n\
269 Cumulative stats are then printed\n\
270 If # is provided, stats will be output every\n\
272 -S, --since file Shows difference between current stats and those in 'file'\n\
273 -l, --list Prints stats in list format\n\
274 --version Show program version\n\
275 --help What you just did\n\
280 static struct option longopts[] =
282 { "acl", 0, 0, 'a' },
283 { "all", 0, 0, 'v' },
284 { "auto", 0, 0, '\3' },
285 { "client", 0, 0, 'c' },
286 { "mounted", 0, 0, 'm' },
287 { "nfs", 0, 0, 'n' },
288 { "rpc", 0, 0, 'r' },
289 { "server", 0, 0, 's' },
290 { "verbose", 0, 0, 'v' },
291 { "zero", 0, 0, 'z' },
292 { "help", 0, 0, '\1' },
293 { "version", 0, 0, '\2' },
294 { "sleep", 2, 0, 'Z' },
295 { "since", 1, 0, 'S' },
296 { "list", 0, 0, 'l' },
302 main(int argc, char **argv)
313 *serverfile = NFSSRVSTAT,
314 *clientfile = NFSCLTSTAT;
316 struct statinfo *serverinfo = srvinfo,
317 *serverinfo_tmp = srvinfo_old,
318 *clientinfo = cltinfo,
319 *clientinfo_tmp = cltinfo_old;
321 struct sigaction act = {
322 .sa_handler = unpause,
323 .sa_flags = SA_ONESHOT,
326 if ((progname = strrchr(argv[0], '/')))
331 while ((c = getopt_long(argc, argv, "234acmno:Z::S:vrslz\1\2", longopts, NULL)) != EOF) {
334 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
340 opt_prt |= PRNT_CALLS;
343 if (!strcmp(optarg, "nfs"))
344 opt_prt |= PRNT_CALLS;
345 else if (!strcmp(optarg, "rpc"))
347 else if (!strcmp(optarg, "net"))
349 else if (!strcmp(optarg, "rc"))
351 else if (!strcmp(optarg, "fh"))
353 else if (!strcmp(optarg, "all"))
354 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
356 fprintf(stderr, "nfsstat: unknown category: "
364 sleep_time = atoi(optarg);
375 opt_prt |= versions[c - '2'];
381 opt_prt |= PRNT_AUTO;
393 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
394 "not yet supported\n");
397 return mounts(MOUNTSFILE);
402 fprintf(stdout, "nfsstat: " VERSION "\n");
405 printf("Try `%s --help' for more information.\n", progname);
411 opt_srv = opt_clt = 1;
414 if (!(opt_srv + opt_clt))
415 opt_srv = opt_clt = 1;
416 if (!(opt_prt & 0xfff)) {
417 opt_prt |= PRNT_CALLS + PRNT_RPC;
419 if (!(opt_prt & 0xe000)) {
420 opt_prt |= PRNT_AUTO;
422 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
424 "You requested file handle or request cache "
425 "statistics while using the -c option.\n"
426 "This information is available only for the NFS "
430 if (opt_since || opt_sleep) {
431 serverinfo = srvinfo_old;
432 serverinfo_tmp = srvinfo;
433 clientinfo = cltinfo_old;
434 clientinfo_tmp = cltinfo;
438 get_stats(serverfile, serverinfo, &opt_srv, opt_clt, 1);
440 get_stats(clientfile, clientinfo, &opt_clt, opt_srv, 0);
442 if (opt_sleep && !sleep_time) {
443 starttime = time(NULL);
444 printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
445 if (sigaction(SIGINT, &act, NULL) != 0) {
446 fprintf(stderr, "Error: couldn't register for signal and pause.\n");
452 if (opt_since || opt_sleep) {
454 get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, 1);
455 diff_stats(serverinfo_tmp, serverinfo, 1);
458 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
459 diff_stats(clientinfo_tmp, clientinfo, 0);
465 get_stats(NFSSRVSTAT, serverinfo_tmp , &opt_srv, opt_clt, 1);
466 diff_stats(serverinfo_tmp, serverinfo, 1);
469 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
470 diff_stats(clientinfo_tmp, clientinfo, 0);
473 print_stats_list(opt_srv, opt_clt, opt_prt);
475 print_all_stats(opt_srv, opt_clt, opt_prt);
480 update_old_counters(serverinfo_tmp, serverinfo);
482 update_old_counters(clientinfo_tmp, clientinfo);
488 print_stats_list(opt_srv, opt_clt, opt_prt);
490 print_all_stats(opt_srv, opt_clt, opt_prt);
498 print_all_stats (int opt_srv, int opt_clt, int opt_prt)
500 print_server_stats(opt_srv, opt_prt);
501 print_client_stats(opt_clt, opt_prt);
505 print_server_stats(int opt_srv, int opt_prt)
510 if (opt_prt & PRNT_NET) {
511 if (opt_sleep && !has_rpcstats(srvnetinfo, 4)) {
513 print_numbers( LABEL_srvnet
514 "packets udp tcp tcpconn\n",
519 if (opt_prt & PRNT_RPC) {
520 if (opt_sleep && !has_rpcstats(srvrpcinfo, 5)) {
523 print_numbers(LABEL_srvrpc
524 "calls badcalls badauth badclnt xdrcall\n",
529 if (opt_prt & PRNT_RC) {
530 if (opt_sleep && !has_rpcstats(srvrcinfo, 3)) {
533 print_numbers(LABEL_srvrc
534 "hits misses nocache\n",
541 * 2.2 puts all fh-related info after the 'rc' header
542 * 2.4 puts all fh-related info after the 'fh' header, but relocates
543 * 'stale' to the start and swaps dir and nondir :-(
544 * We preseve the 2.2 order
546 if (opt_prt & PRNT_FH) {
547 if (get_stat_info("fh", srvinfo)) { /* >= 2.4 */
548 int t = srvfhinfo[3];
549 srvfhinfo[3]=srvfhinfo[4];
552 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
556 "lookup anon ncachedir ncachedir stale\n",
561 "lookup anon ncachedir ncachedir stale\n",
565 if (opt_prt & PRNT_CALLS) {
566 if ((opt_prt & PRNT_V2) ||
567 ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
568 if (opt_sleep && !has_stats(srvproc2info)) {
571 print_callstats(LABEL_srvproc2,
572 nfsv2name, srvproc2info + 1,
573 sizeof(nfsv2name)/sizeof(char *));
576 if ((opt_prt & PRNT_V3) ||
577 ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
578 if (opt_sleep && !has_stats(srvproc3info)) {
581 print_callstats(LABEL_srvproc3,
582 nfsv3name, srvproc3info + 1,
583 sizeof(nfsv3name)/sizeof(char *));
586 if ((opt_prt & PRNT_V4) ||
587 ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
588 if (opt_sleep && !has_stats(srvproc4info)) {
591 print_callstats( LABEL_srvproc4,
592 nfssrvproc4name, srvproc4info + 1,
593 sizeof(nfssrvproc4name)/sizeof(char *));
594 print_callstats(LABEL_srvproc4ops,
595 nfssrvproc4opname, srvproc4opsinfo + 1,
596 sizeof(nfssrvproc4opname)/sizeof(char *));
602 print_client_stats(int opt_clt, int opt_prt)
607 if (opt_prt & PRNT_NET) {
608 if (opt_sleep && !has_rpcstats(cltnetinfo, 4)) {
611 print_numbers(LABEL_cltnet
612 "packets udp tcp tcpconn\n",
617 if (opt_prt & PRNT_RPC) {
618 if (opt_sleep && !has_rpcstats(cltrpcinfo, 3)) {
621 print_numbers(LABEL_cltrpc
622 "calls retrans authrefrsh\n",
627 if (opt_prt & PRNT_CALLS) {
628 if ((opt_prt & PRNT_V2) ||
629 ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
630 if (opt_sleep && !has_stats(cltproc2info)) {
633 print_callstats(LABEL_cltproc2,
634 nfsv2name, cltproc2info + 1,
635 sizeof(nfsv2name)/sizeof(char *));
638 if ((opt_prt & PRNT_V3) ||
639 ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
640 if (opt_sleep && !has_stats(cltproc3info)) {
643 print_callstats(LABEL_cltproc3,
644 nfsv3name, cltproc3info + 1,
645 sizeof(nfsv3name)/sizeof(char *));
648 if ((opt_prt & PRNT_V4) ||
649 ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
650 if (opt_sleep && !has_stats(cltproc4info)) {
653 print_callstats(LABEL_cltproc4,
654 nfscltproc4name, cltproc4info + 1,
655 sizeof(nfscltproc4name)/sizeof(char *));
662 print_clnt_list(int opt_prt)
664 if (opt_prt & PRNT_CALLS) {
665 if ((opt_prt & PRNT_V2) ||
666 ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
667 if (opt_sleep && !has_stats(cltproc2info)) {
670 print_callstats_list("nfs v2 client",
671 nfsv2name, cltproc2info + 1,
672 sizeof(nfsv2name)/sizeof(char *));
675 if ((opt_prt & PRNT_V3) ||
676 ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
677 if (opt_sleep && !has_stats(cltproc3info)) {
680 print_callstats_list("nfs v3 client",
681 nfsv3name, cltproc3info + 1,
682 sizeof(nfsv3name)/sizeof(char *));
685 if ((opt_prt & PRNT_V4) ||
686 ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
687 if (opt_sleep && !has_stats(cltproc4info)) {
690 print_callstats_list("nfs v4 ops",
691 nfssrvproc4opname, srvproc4opsinfo + 1,
692 sizeof(nfssrvproc4opname)/sizeof(char *));
693 print_callstats_list("nfs v4 client",
694 nfscltproc4name, cltproc4info + 1,
695 sizeof(nfscltproc4name)/sizeof(char *));
701 print_serv_list(int opt_prt)
703 if (opt_prt & PRNT_CALLS) {
704 if ((opt_prt & PRNT_V2) ||
705 ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
706 if (opt_sleep && !has_stats(srvproc2info)) {
709 print_callstats_list("nfs v2 server",
710 nfsv2name, srvproc2info + 1,
711 sizeof(nfsv2name)/sizeof(char *));
714 if ((opt_prt & PRNT_V3) ||
715 ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
716 if (opt_sleep && !has_stats(srvproc3info)) {
719 print_callstats_list("nfs v3 server",
720 nfsv3name, srvproc3info + 1,
721 sizeof(nfsv3name)/sizeof(char *));
724 if ((opt_prt & PRNT_V4) ||
725 ((opt_prt & PRNT_AUTO) && has_stats(srvproc4opsinfo))) {
726 if (opt_sleep && !has_stats(srvproc4info)) {
729 print_callstats_list("nfs v4 ops",
730 nfssrvproc4opname, srvproc4opsinfo + 1,
731 sizeof(nfssrvproc4opname)/sizeof(char *));
737 print_stats_list(int opt_srv, int opt_clt, int opt_prt)
740 print_serv_list(opt_prt);
743 print_clnt_list(opt_prt);
747 get_stat_info(const char *sp, struct statinfo *statp)
751 for (ip = statp; ip->tag; ip++) {
752 if (!strcmp(sp, ip->tag))
760 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
765 for (i = 0; i < nr; i++)
766 printf("%s%-8u", i? " " : "", info[i]);
771 print_callstats(const char *hdr, const char **names,
772 unsigned int *info, unsigned int nr)
774 unsigned long long total;
775 unsigned long long pct;
779 for (i = 0, total = 0; i < nr; i++)
783 for (i = 0; i < nr; i += 6) {
784 for (j = 0; j < 6 && i + j < nr; j++)
785 printf("%-13s", names[i+j]);
787 for (j = 0; j < 6 && i + j < nr; j++) {
788 pct = ((unsigned long long) info[i+j]*100)/total;
789 printf("%-8u%3llu%% ", info[i+j], pct);
797 print_callstats_list(const char *hdr, const char **names,
798 unsigned int *callinfo, unsigned int nr)
800 unsigned long long calltotal;
803 for (i = 0, calltotal = 0; i < nr; i++) {
804 calltotal += callinfo[i];
808 printf("%13s %13s %8llu \n", hdr, "total:", calltotal);
809 printf("------------- ------------- --------\n");
810 for (i = 0; i < nr; i++) {
812 printf("%13s %12s: %8u \n", hdr, names[i], callinfo[i]);
819 /* returns 0 on success, 1 otherwise */
821 parse_raw_statfile(const char *name, struct statinfo *statp)
823 char buffer[4096], *next;
826 /* Being unable to read e.g. the nfsd stats file shouldn't
827 * be a fatal error -- it usually means the module isn't loaded.
829 if ((fp = fopen(name, "r")) == NULL) {
830 // fprintf(stderr, "Warning: %s: %m\n", name);
834 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
836 char *sp, *line = buffer;
838 unsigned int total = 0;
840 if ((next = strchr(line, '\n')) != NULL)
842 if (!(sp = strtok(line, " \t")))
845 ip = get_stat_info(sp, statp);
851 for (i = 0; i < cnt; i++) {
852 if (!(sp = strtok(NULL, " \t")))
854 ip->valptr[i] = (unsigned int) strtoul(sp, NULL, 0);
855 total += ip->valptr[i];
857 ip->valptr[cnt - 1] = total;
864 /* returns 0 on success, 1 otherwise */
866 parse_pretty_statfile(const char *filename, struct statinfo *info)
868 int numvals, curindex, numconsumed, n, err = 1;
870 char buf[4096], *bufp, *fmt, is_proc;
874 if ((fp = fopen(filename, "r")) == NULL)
875 //err(2, "Unable to open statfile '%s'.\n", filename);
878 while (fgets(buf, sizeof(buf), fp) != NULL) {
879 for (ip = info; ip->tag; ip++) {
880 if (strcmp(buf, ip->label))
884 numvals = ip->nrvals - 1;
885 is_proc = strncmp("proc", ip->tag, 4) ? 0 : 1;
887 fmt = " %u %*u%% %n";
895 /* get (and skip) header */
896 if (fgets(buf, sizeof(buf), fp) == NULL) {
897 fprintf(stderr, "Failed to locate header after "
898 "label for '%s' in %s.\n",
902 /* no header -- done with this "tag" */
904 ip->valptr[numvals] = sum;
908 if (fgets(buf, sizeof(buf), fp) == NULL) {
909 fprintf(stderr, "Failed to locate stats after "
910 "header for '%s' in %s.\n",
915 for (; curindex < numvals; curindex++) {
916 n = sscanf(bufp, fmt, &ip->valptr[curindex],
924 sum += ip->valptr[curindex];
938 mounts(const char *name)
940 char buffer[4096], *next;
943 /* Being unable to read e.g. the nfsd stats file shouldn't
944 * be a fatal error -- it usually means the module isn't loaded.
946 if ((fp = fopen(name, "r")) == NULL) {
947 fprintf(stderr, "Warning: %s: %m\n", name);
951 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
953 char *device, *mount, *type, *flags;
955 if ((next = strchr(line, '\n')) != NULL)
958 if (!(device = strtok(line, " \t")))
961 if (!(mount = strtok(NULL, " \t")))
964 if (!(type = strtok(NULL, " \t")))
967 if (strcmp(type, "nfs") && strcmp(type,"nfs4")) {
971 if (!(flags = strtok(NULL, " \t")))
974 printf("%s from %s\n", mount, device);
975 printf(" Flags:\t%s\n", flags);
986 get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
992 char *label = is_srv ? "Server" : "Client";
994 /* try to guess what type of stat file we're dealing with */
995 if ((fp = fopen(file, "r")) == NULL)
997 if (fgets(buf, 10, fp) == NULL)
999 if (!strncmp(buf, "net ", 4)) {
1000 /* looks like raw client stats */
1002 fprintf(stderr, "Warning: no server info present in "
1003 "raw client stats file.\n");
1006 err = parse_raw_statfile(file, info);
1007 } else if (!strncmp(buf, "rc ", 3)) {
1008 /* looks like raw server stats */
1010 fprintf(stderr, "Warning: no client info present in "
1011 "raw server stats file.\n");
1014 err = parse_raw_statfile(file, info);
1016 /* looks like pretty client and server stats */
1017 err = parse_pretty_statfile(file, info);
1023 fprintf(stderr, "Error: No %s Stats (%s: %m). \n",
1032 * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
1033 * denotes the number of subsequent entries. statinfo value arrays contain an additional
1034 * field at the end which contains the sum of all previous elements in the array -- so,
1035 * there are stats if the sum's greater than the entry-count.
1038 has_stats(const unsigned int *info)
1040 return (info[0] && info[info[0] + 1] > info[0]);
1043 has_rpcstats(const unsigned int *info, int size)
1047 for (i=0, cnt=0; i < size; i++)
1053 * take the difference of each individual stat value in 'new' and 'old'
1054 * and store the results back into 'new'
1057 diff_stats(struct statinfo *new, struct statinfo *old, int is_srv)
1059 int i, j, nodiff_first_index, should_diff;
1062 * Different stat types have different formats in the /proc
1063 * files: for the proc2/3/4-type stats, the first entry has
1064 * the total number of subsequent entries; one does not want
1065 * to diff that first entry. The other stat types aren't like
1066 * this. So, we diff a given entry if it's not of one of the
1067 * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
1068 * the first entry ("j" > 0).
1070 nodiff_first_index = 2 + (2 * is_srv);
1072 for (i = 0; old[i].tag; i++) {
1073 for (j = 0; j < new[i].nrvals; j++) {
1074 should_diff = (i < nodiff_first_index || j > 0);
1076 new[i].valptr[j] -= old[i].valptr[j];
1080 * Make sure that the "totals" entry (last value in
1081 * each stat array) for the procX-type stats has the
1082 * "numentries" entry's (first value in procX-type
1083 * stat arrays) constant value added-back after the
1084 * diff -- i.e., it should always be included in the
1087 if (!strncmp("proc", new[i].tag, 4) && old[i].valptr[0])
1088 new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
1096 int minutes, seconds;
1099 endtime = time(NULL);
1100 time_diff = difftime(endtime, starttime);
1101 minutes = time_diff / 60;
1102 seconds = (int)time_diff % 60;
1103 printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds);
1107 update_old_counters(struct statinfo *new, struct statinfo *old)
1110 for (z = 0; old[z].tag; z++)
1111 for (i = 0; i <= old[z].nrvals; i++)
1112 old[z].valptr[i] += new[z].valptr[i];