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[50], cltproc4info_old[50]; /* NFSv4 call counts ([0] == 48) */
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[48] = {
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 */
116 /* nfsv4.1 pnfs client ops to data server only */
121 static const char * nfssrvproc4opname[59] = {
122 "op0-unused", "op1-unused", "op2-future", "access", "close", "commit",
123 "create", "delegpurge", "delegreturn", "getattr", "getfh", "link",
124 "lock", "lockt", "locku", "lookup", "lookup_root", "nverify",
125 "open", "openattr", "open_conf", "open_dgrd", "putfh", "putpubfh",
126 "putrootfh", "read", "readdir", "readlink", "remove", "rename",
127 "renew", "restorefh", "savefh", "secinfo", "setattr", "setcltid",
128 "setcltidconf", "verify", "write", "rellockowner",
129 /* nfsv4.1 server ops */
151 #define LABEL_srvnet "Server packet stats:\n"
152 #define LABEL_srvrpc "Server rpc stats:\n"
153 #define LABEL_srvrc "Server reply cache:\n"
154 #define LABEL_srvfh "Server file handle cache:\n"
155 #define LABEL_srvproc2 "Server nfs v2:\n"
156 #define LABEL_srvproc3 "Server nfs v3:\n"
157 #define LABEL_srvproc4 "Server nfs v4:\n"
158 #define LABEL_srvproc4ops "Server nfs v4 operations:\n"
159 #define LABEL_cltnet "Client packet stats:\n"
160 #define LABEL_cltrpc "Client rpc stats:\n"
161 #define LABEL_cltproc2 "Client nfs v2:\n"
162 #define LABEL_cltproc3 "Client nfs v3:\n"
163 #define LABEL_cltproc4 "Client nfs v4:\n"
165 typedef struct statinfo {
169 unsigned int * valptr;
173 * We now build the arrays of statinfos using macros, which will make it easier
174 * to add new variables for --sleep. e.g., SRV(net) expands into the struct
175 * statinfo: { "net", "Server packet stats:\n", 5, srvnetinfo }
177 #define ARRAYSIZE(x) sizeof(x)/sizeof(*x)
178 #define STATINFO(k, t, s...) { #t, LABEL_##k##t, ARRAYSIZE(k##t##info##s), k##t##info##s }
179 #define SRV(t, s...) STATINFO(srv, t, s)
180 #define CLT(t, s...) STATINFO(clt, t, s)
181 #define DECLARE_SRV(n, s...) static statinfo n##s[] = { \
190 { NULL, NULL, 0, NULL }\
192 #define DECLARE_CLT(n, s...) static statinfo n##s[] = { \
198 { NULL, NULL, 0, NULL }\
200 DECLARE_SRV(srvinfo);
201 DECLARE_SRV(srvinfo, _old);
202 DECLARE_CLT(cltinfo);
203 DECLARE_CLT(cltinfo, _old);
205 static void print_all_stats(int, int, int);
206 static void print_server_stats(int, int);
207 static void print_client_stats(int, int);
208 static void print_stats_list(int, int, int);
209 static void print_numbers(const char *, unsigned int *,
211 static void print_callstats(const char *, const char **,
212 unsigned int *, unsigned int);
213 static void print_callstats_list(const char *, const char **,
214 unsigned int *, unsigned int);
215 static int parse_raw_statfile(const char *, struct statinfo *);
216 static int parse_pretty_statfile(const char *, struct statinfo *);
218 static statinfo *get_stat_info(const char *, struct statinfo *);
220 static int mounts(const char *);
222 static void get_stats(const char *, struct statinfo *, int *, int,
224 static int has_stats(const unsigned int *);
225 static int has_rpcstats(const unsigned int *, int);
226 static void diff_stats(struct statinfo *, struct statinfo *, int);
227 static void unpause(int);
228 static void update_old_counters(struct statinfo *, struct statinfo *);
230 static time_t starttime;
232 #define PRNT_CALLS 0x0001
233 #define PRNT_RPC 0x0002
234 #define PRNT_NET 0x0004
235 #define PRNT_FH 0x0008
236 #define PRNT_RC 0x0010
237 #define PRNT_AUTO 0x1000
238 #define PRNT_V2 0x2000
239 #define PRNT_V3 0x4000
240 #define PRNT_V4 0x8000
241 #define PRNT_ALL 0x0fff
249 void usage(char *name)
251 printf("Usage: %s [OPTION]...\n\
253 -m, --mounts Show statistics on mounted NFS filesystems\n\
254 -c, --client Show NFS client statistics\n\
255 -s, --server Show NFS server statistics\n\
256 -2 Show NFS version 2 statistics\n\
257 -3 Show NFS version 3 statistics\n\
258 -4 Show NFS version 4 statistics\n\
259 -o [facility] Show statistics on particular facilities.\n\
260 nfs NFS protocol information\n\
261 rpc General RPC information\n\
262 net Network layer statistics\n\
263 fh Usage information on the server's file handle cache\n\
264 rc Usage information on the server's request reply cache\n\
265 all Select all of the above\n\
266 -v, --verbose, --all Same as '-o all'\n\
267 -r, --rpc Show RPC statistics\n\
268 -n, --nfs Show NFS statistics\n\
269 -Z[#], --sleep[=#] Collects stats until interrupted.\n\
270 Cumulative stats are then printed\n\
271 If # is provided, stats will be output every\n\
273 -S, --since file Shows difference between current stats and those in 'file'\n\
274 -l, --list Prints stats in list format\n\
275 --version Show program version\n\
276 --help What you just did\n\
281 static struct option longopts[] =
283 { "acl", 0, 0, 'a' },
284 { "all", 0, 0, 'v' },
285 { "auto", 0, 0, '\3' },
286 { "client", 0, 0, 'c' },
287 { "mounted", 0, 0, 'm' },
288 { "nfs", 0, 0, 'n' },
289 { "rpc", 0, 0, 'r' },
290 { "server", 0, 0, 's' },
291 { "verbose", 0, 0, 'v' },
292 { "zero", 0, 0, 'z' },
293 { "help", 0, 0, '\1' },
294 { "version", 0, 0, '\2' },
295 { "sleep", 2, 0, 'Z' },
296 { "since", 1, 0, 'S' },
297 { "list", 0, 0, 'l' },
303 main(int argc, char **argv)
314 *serverfile = NFSSRVSTAT,
315 *clientfile = NFSCLTSTAT;
317 struct statinfo *serverinfo = srvinfo,
318 *serverinfo_tmp = srvinfo_old,
319 *clientinfo = cltinfo,
320 *clientinfo_tmp = cltinfo_old;
322 struct sigaction act = {
323 .sa_handler = unpause,
324 .sa_flags = SA_ONESHOT,
327 if ((progname = strrchr(argv[0], '/')))
332 while ((c = getopt_long(argc, argv, "234acmno:Z::S:vrslz\1\2", longopts, NULL)) != EOF) {
335 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
341 opt_prt |= PRNT_CALLS;
344 if (!strcmp(optarg, "nfs"))
345 opt_prt |= PRNT_CALLS;
346 else if (!strcmp(optarg, "rpc"))
348 else if (!strcmp(optarg, "net"))
350 else if (!strcmp(optarg, "rc"))
352 else if (!strcmp(optarg, "fh"))
354 else if (!strcmp(optarg, "all"))
355 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
357 fprintf(stderr, "nfsstat: unknown category: "
365 sleep_time = atoi(optarg);
376 opt_prt |= versions[c - '2'];
382 opt_prt |= PRNT_AUTO;
394 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
395 "not yet supported\n");
398 return mounts(MOUNTSFILE);
403 fprintf(stdout, "nfsstat: " VERSION "\n");
406 printf("Try `%s --help' for more information.\n", progname);
412 opt_srv = opt_clt = 1;
415 if (!(opt_srv + opt_clt))
416 opt_srv = opt_clt = 1;
417 if (!(opt_prt & 0xfff)) {
418 opt_prt |= PRNT_CALLS + PRNT_RPC;
420 if (!(opt_prt & 0xe000)) {
421 opt_prt |= PRNT_AUTO;
423 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
425 "You requested file handle or request cache "
426 "statistics while using the -c option.\n"
427 "This information is available only for the NFS "
431 if (opt_since || opt_sleep) {
432 serverinfo = srvinfo_old;
433 serverinfo_tmp = srvinfo;
434 clientinfo = cltinfo_old;
435 clientinfo_tmp = cltinfo;
439 get_stats(serverfile, serverinfo, &opt_srv, opt_clt, 1);
441 get_stats(clientfile, clientinfo, &opt_clt, opt_srv, 0);
443 if (opt_sleep && !sleep_time) {
444 starttime = time(NULL);
445 printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
446 if (sigaction(SIGINT, &act, NULL) != 0) {
447 fprintf(stderr, "Error: couldn't register for signal and pause.\n");
453 if (opt_since || opt_sleep) {
455 get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, 1);
456 diff_stats(serverinfo_tmp, serverinfo, 1);
459 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
460 diff_stats(clientinfo_tmp, clientinfo, 0);
466 get_stats(NFSSRVSTAT, serverinfo_tmp , &opt_srv, opt_clt, 1);
467 diff_stats(serverinfo_tmp, serverinfo, 1);
470 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
471 diff_stats(clientinfo_tmp, clientinfo, 0);
474 print_stats_list(opt_srv, opt_clt, opt_prt);
476 print_all_stats(opt_srv, opt_clt, opt_prt);
481 update_old_counters(serverinfo_tmp, serverinfo);
483 update_old_counters(clientinfo_tmp, clientinfo);
489 print_stats_list(opt_srv, opt_clt, opt_prt);
491 print_all_stats(opt_srv, opt_clt, opt_prt);
499 print_all_stats (int opt_srv, int opt_clt, int opt_prt)
501 print_server_stats(opt_srv, opt_prt);
502 print_client_stats(opt_clt, opt_prt);
506 print_server_stats(int opt_srv, int opt_prt)
511 if (opt_prt & PRNT_NET) {
512 if (opt_sleep && !has_rpcstats(srvnetinfo, 4)) {
514 print_numbers( LABEL_srvnet
515 "packets udp tcp tcpconn\n",
520 if (opt_prt & PRNT_RPC) {
521 if (opt_sleep && !has_rpcstats(srvrpcinfo, 5)) {
524 print_numbers(LABEL_srvrpc
525 "calls badcalls badauth badclnt xdrcall\n",
530 if (opt_prt & PRNT_RC) {
531 if (opt_sleep && !has_rpcstats(srvrcinfo, 3)) {
534 print_numbers(LABEL_srvrc
535 "hits misses nocache\n",
542 * 2.2 puts all fh-related info after the 'rc' header
543 * 2.4 puts all fh-related info after the 'fh' header, but relocates
544 * 'stale' to the start and swaps dir and nondir :-(
545 * We preseve the 2.2 order
547 if (opt_prt & PRNT_FH) {
548 if (get_stat_info("fh", srvinfo)) { /* >= 2.4 */
549 int t = srvfhinfo[3];
550 srvfhinfo[3]=srvfhinfo[4];
553 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
557 "lookup anon ncachedir ncachedir stale\n",
562 "lookup anon ncachedir ncachedir stale\n",
566 if (opt_prt & PRNT_CALLS) {
567 if ((opt_prt & PRNT_V2) ||
568 ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
569 if (opt_sleep && !has_stats(srvproc2info)) {
572 print_callstats(LABEL_srvproc2,
573 nfsv2name, srvproc2info + 1,
574 sizeof(nfsv2name)/sizeof(char *));
577 if ((opt_prt & PRNT_V3) ||
578 ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
579 if (opt_sleep && !has_stats(srvproc3info)) {
582 print_callstats(LABEL_srvproc3,
583 nfsv3name, srvproc3info + 1,
584 sizeof(nfsv3name)/sizeof(char *));
587 if ((opt_prt & PRNT_V4) ||
588 ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
589 if (opt_sleep && !has_stats(srvproc4info)) {
592 print_callstats( LABEL_srvproc4,
593 nfssrvproc4name, srvproc4info + 1,
594 sizeof(nfssrvproc4name)/sizeof(char *));
595 print_callstats(LABEL_srvproc4ops,
596 nfssrvproc4opname, srvproc4opsinfo + 1,
597 sizeof(nfssrvproc4opname)/sizeof(char *));
603 print_client_stats(int opt_clt, int opt_prt)
608 if (opt_prt & PRNT_NET) {
609 if (opt_sleep && !has_rpcstats(cltnetinfo, 4)) {
612 print_numbers(LABEL_cltnet
613 "packets udp tcp tcpconn\n",
618 if (opt_prt & PRNT_RPC) {
619 if (opt_sleep && !has_rpcstats(cltrpcinfo, 3)) {
622 print_numbers(LABEL_cltrpc
623 "calls retrans authrefrsh\n",
628 if (opt_prt & PRNT_CALLS) {
629 if ((opt_prt & PRNT_V2) ||
630 ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
631 if (opt_sleep && !has_stats(cltproc2info)) {
634 print_callstats(LABEL_cltproc2,
635 nfsv2name, cltproc2info + 1,
636 sizeof(nfsv2name)/sizeof(char *));
639 if ((opt_prt & PRNT_V3) ||
640 ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
641 if (opt_sleep && !has_stats(cltproc3info)) {
644 print_callstats(LABEL_cltproc3,
645 nfsv3name, cltproc3info + 1,
646 sizeof(nfsv3name)/sizeof(char *));
649 if ((opt_prt & PRNT_V4) ||
650 ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
651 if (opt_sleep && !has_stats(cltproc4info)) {
654 print_callstats(LABEL_cltproc4,
655 nfscltproc4name, cltproc4info + 1,
656 sizeof(nfscltproc4name)/sizeof(char *));
663 print_clnt_list(int opt_prt)
665 if (opt_prt & PRNT_CALLS) {
666 if ((opt_prt & PRNT_V2) ||
667 ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
668 if (opt_sleep && !has_stats(cltproc2info)) {
671 print_callstats_list("nfs v2 client",
672 nfsv2name, cltproc2info + 1,
673 sizeof(nfsv2name)/sizeof(char *));
676 if ((opt_prt & PRNT_V3) ||
677 ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
678 if (opt_sleep && !has_stats(cltproc3info)) {
681 print_callstats_list("nfs v3 client",
682 nfsv3name, cltproc3info + 1,
683 sizeof(nfsv3name)/sizeof(char *));
686 if ((opt_prt & PRNT_V4) ||
687 ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
688 if (opt_sleep && !has_stats(cltproc4info)) {
691 print_callstats_list("nfs v4 ops",
692 nfssrvproc4opname, srvproc4opsinfo + 1,
693 sizeof(nfssrvproc4opname)/sizeof(char *));
694 print_callstats_list("nfs v4 client",
695 nfscltproc4name, cltproc4info + 1,
696 sizeof(nfscltproc4name)/sizeof(char *));
702 print_serv_list(int opt_prt)
704 if (opt_prt & PRNT_CALLS) {
705 if ((opt_prt & PRNT_V2) ||
706 ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
707 if (opt_sleep && !has_stats(srvproc2info)) {
710 print_callstats_list("nfs v2 server",
711 nfsv2name, srvproc2info + 1,
712 sizeof(nfsv2name)/sizeof(char *));
715 if ((opt_prt & PRNT_V3) ||
716 ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
717 if (opt_sleep && !has_stats(srvproc3info)) {
720 print_callstats_list("nfs v3 server",
721 nfsv3name, srvproc3info + 1,
722 sizeof(nfsv3name)/sizeof(char *));
725 if ((opt_prt & PRNT_V4) ||
726 ((opt_prt & PRNT_AUTO) && has_stats(srvproc4opsinfo))) {
727 if (opt_sleep && !has_stats(srvproc4info)) {
730 print_callstats_list("nfs v4 ops",
731 nfssrvproc4opname, srvproc4opsinfo + 1,
732 sizeof(nfssrvproc4opname)/sizeof(char *));
738 print_stats_list(int opt_srv, int opt_clt, int opt_prt)
741 print_serv_list(opt_prt);
744 print_clnt_list(opt_prt);
748 get_stat_info(const char *sp, struct statinfo *statp)
752 for (ip = statp; ip->tag; ip++) {
753 if (!strcmp(sp, ip->tag))
761 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
766 for (i = 0; i < nr; i++)
767 printf("%s%-8u", i? " " : "", info[i]);
772 print_callstats(const char *hdr, const char **names,
773 unsigned int *info, unsigned int nr)
775 unsigned long long total;
776 unsigned long long pct;
780 for (i = 0, total = 0; i < nr; i++)
784 for (i = 0; i < nr; i += 6) {
785 for (j = 0; j < 6 && i + j < nr; j++)
786 printf("%-13s", names[i+j]);
788 for (j = 0; j < 6 && i + j < nr; j++) {
789 pct = ((unsigned long long) info[i+j]*100)/total;
790 printf("%-8u%3llu%% ", info[i+j], pct);
798 print_callstats_list(const char *hdr, const char **names,
799 unsigned int *callinfo, unsigned int nr)
801 unsigned long long calltotal;
804 for (i = 0, calltotal = 0; i < nr; i++) {
805 calltotal += callinfo[i];
809 printf("%13s %13s %8llu \n", hdr, "total:", calltotal);
810 printf("------------- ------------- --------\n");
811 for (i = 0; i < nr; i++) {
813 printf("%13s %12s: %8u \n", hdr, names[i], callinfo[i]);
820 /* returns 0 on success, 1 otherwise */
822 parse_raw_statfile(const char *name, struct statinfo *statp)
824 char buffer[4096], *next;
827 /* Being unable to read e.g. the nfsd stats file shouldn't
828 * be a fatal error -- it usually means the module isn't loaded.
830 if ((fp = fopen(name, "r")) == NULL) {
831 // fprintf(stderr, "Warning: %s: %m\n", name);
835 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
837 char *sp, *line = buffer;
839 unsigned int total = 0;
841 if ((next = strchr(line, '\n')) != NULL)
843 if (!(sp = strtok(line, " \t")))
846 ip = get_stat_info(sp, statp);
852 for (i = 0; i < cnt; i++) {
853 if (!(sp = strtok(NULL, " \t")))
855 ip->valptr[i] = (unsigned int) strtoul(sp, NULL, 0);
856 total += ip->valptr[i];
858 ip->valptr[cnt - 1] = total;
865 /* returns 0 on success, 1 otherwise */
867 parse_pretty_statfile(const char *filename, struct statinfo *info)
869 int numvals, curindex, numconsumed, n, err = 1;
871 char buf[4096], *bufp, *fmt, is_proc;
875 if ((fp = fopen(filename, "r")) == NULL)
876 //err(2, "Unable to open statfile '%s'.\n", filename);
879 while (fgets(buf, sizeof(buf), fp) != NULL) {
880 for (ip = info; ip->tag; ip++) {
881 if (strcmp(buf, ip->label))
885 numvals = ip->nrvals - 1;
886 is_proc = strncmp("proc", ip->tag, 4) ? 0 : 1;
888 fmt = " %u %*u%% %n";
896 /* get (and skip) header */
897 if (fgets(buf, sizeof(buf), fp) == NULL) {
898 fprintf(stderr, "Failed to locate header after "
899 "label for '%s' in %s.\n",
903 /* no header -- done with this "tag" */
905 ip->valptr[numvals] = sum;
909 if (fgets(buf, sizeof(buf), fp) == NULL) {
910 fprintf(stderr, "Failed to locate stats after "
911 "header for '%s' in %s.\n",
916 for (; curindex < numvals; curindex++) {
917 n = sscanf(bufp, fmt, &ip->valptr[curindex],
925 sum += ip->valptr[curindex];
939 mounts(const char *name)
941 char buffer[4096], *next;
944 /* Being unable to read e.g. the nfsd stats file shouldn't
945 * be a fatal error -- it usually means the module isn't loaded.
947 if ((fp = fopen(name, "r")) == NULL) {
948 fprintf(stderr, "Warning: %s: %m\n", name);
952 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
954 char *device, *mount, *type, *flags;
956 if ((next = strchr(line, '\n')) != NULL)
959 if (!(device = strtok(line, " \t")))
962 if (!(mount = strtok(NULL, " \t")))
965 if (!(type = strtok(NULL, " \t")))
968 if (strcmp(type, "nfs") && strcmp(type,"nfs4")) {
972 if (!(flags = strtok(NULL, " \t")))
975 printf("%s from %s\n", mount, device);
976 printf(" Flags:\t%s\n", flags);
987 get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
993 char *label = is_srv ? "Server" : "Client";
995 /* try to guess what type of stat file we're dealing with */
996 if ((fp = fopen(file, "r")) == NULL)
998 if (fgets(buf, 10, fp) == NULL)
1000 if (!strncmp(buf, "net ", 4)) {
1001 /* looks like raw client stats */
1003 fprintf(stderr, "Warning: no server info present in "
1004 "raw client stats file.\n");
1007 err = parse_raw_statfile(file, info);
1008 } else if (!strncmp(buf, "rc ", 3)) {
1009 /* looks like raw server stats */
1011 fprintf(stderr, "Warning: no client info present in "
1012 "raw server stats file.\n");
1015 err = parse_raw_statfile(file, info);
1017 /* looks like pretty client and server stats */
1018 err = parse_pretty_statfile(file, info);
1024 fprintf(stderr, "Error: No %s Stats (%s: %m). \n",
1033 * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
1034 * denotes the number of subsequent entries. statinfo value arrays contain an additional
1035 * field at the end which contains the sum of all previous elements in the array -- so,
1036 * there are stats if the sum's greater than the entry-count.
1039 has_stats(const unsigned int *info)
1041 return (info[0] && info[info[0] + 1] > info[0]);
1044 has_rpcstats(const unsigned int *info, int size)
1048 for (i=0, cnt=0; i < size; i++)
1054 * take the difference of each individual stat value in 'new' and 'old'
1055 * and store the results back into 'new'
1058 diff_stats(struct statinfo *new, struct statinfo *old, int is_srv)
1060 int i, j, nodiff_first_index, should_diff;
1063 * Different stat types have different formats in the /proc
1064 * files: for the proc2/3/4-type stats, the first entry has
1065 * the total number of subsequent entries; one does not want
1066 * to diff that first entry. The other stat types aren't like
1067 * this. So, we diff a given entry if it's not of one of the
1068 * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
1069 * the first entry ("j" > 0).
1071 nodiff_first_index = 2 + (2 * is_srv);
1073 for (i = 0; old[i].tag; i++) {
1074 for (j = 0; j < new[i].nrvals; j++) {
1075 should_diff = (i < nodiff_first_index || j > 0);
1077 new[i].valptr[j] -= old[i].valptr[j];
1081 * Make sure that the "totals" entry (last value in
1082 * each stat array) for the procX-type stats has the
1083 * "numentries" entry's (first value in procX-type
1084 * stat arrays) constant value added-back after the
1085 * diff -- i.e., it should always be included in the
1088 if (!strncmp("proc", new[i].tag, 4) && old[i].valptr[0])
1089 new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
1097 int minutes, seconds;
1100 endtime = time(NULL);
1101 time_diff = difftime(endtime, starttime);
1102 minutes = time_diff / 60;
1103 seconds = (int)time_diff % 60;
1104 printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds);
1108 update_old_counters(struct statinfo *new, struct statinfo *old)
1111 for (z = 0; old[z].tag; z++)
1112 for (i = 0; i <= old[z].nrvals; i++)
1113 old[z].valptr[i] += new[z].valptr[i];