nfsstat: Remove some pointer comparisons against global statinfos.
[nfs-utils.git] / utils / nfsstat / nfsstat.c
1 /*
2  * nfsstat.c            Output NFS statistics
3  *
4  * Copyright (C) 1995-2005 Olaf Kirch <okir@suse.de>
5  */
6
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #define NFSSRVSTAT      "/proc/net/rpc/nfsd"
12 #define NFSCLTSTAT      "/proc/net/rpc/nfs"
13
14 #define MOUNTSFILE      "/proc/mounts"
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <getopt.h>
20 #include <string.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <signal.h>
24 #include <time.h>
25
26 #define MAXNRVALS       32
27
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[37], cltproc4info_old[37]; /* NFSv4 call counts ([0] == 35) */
34 static unsigned int     srvproc4opsinfo[42], srvproc4opsinfo_old[42];   /* NFSv4 call counts ([0] == 40) */
35 static unsigned int     srvnetinfo[5], srvnetinfo_old[5];       /* 0  # of received packets
36                                                                  * 1  UDP packets
37                                                                  * 2  TCP packets
38                                                                  * 3  TCP connections
39                                                                  */
40 static unsigned int     cltnetinfo[5], cltnetinfo_old[5];       /* 0  # of received packets
41                                                                  * 1  UDP packets
42                                                                  * 2  TCP packets
43                                                                  * 3  TCP connections
44                                                                  */
45
46 static unsigned int     srvrpcinfo[6], srvrpcinfo_old[6];       /* 0  total # of RPC calls
47                                                                  * 1  total # of bad calls
48                                                                  * 2  bad format
49                                                                  * 3  authentication failed
50                                                                  * 4  unknown client
51                                                                  */
52 static unsigned int     cltrpcinfo[4], cltrpcinfo_old[4];       /* 0  total # of RPC calls
53                                                                  * 1  retransmitted calls
54                                                                  * 2  cred refreshs
55                                                                  */
56
57 static unsigned int     srvrcinfo[9], srvrcinfo_old[9];         /* 0  repcache hits
58                                                                  * 1  repcache hits
59                                                                  * 2  uncached reqs
60                                                                  * (for pre-2.4 kernels:)
61                                                                  * 3  FH lookups
62                                                                  * 4  'anon' FHs
63                                                                  * 5  noncached non-directories
64                                                                  * 6  noncached directories
65                                                                  * 7  stale
66                                                                  */
67
68 static unsigned int     srvfhinfo[7], srvfhinfo_old[7];         /* (for kernels >= 2.4.0)
69                                                                  * 0  stale
70                                                                  * 1  FH lookups
71                                                                  * 2  'anon' FHs
72                                                                  * 3  noncached directories
73                                                                  * 4  noncached non-directories
74                                                                  * leave hole to relocate stale for order
75                                                                  *    compatability.
76                                                                  */
77
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"
82 };
83
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"
89 };
90
91 static const char *     nfssrvproc4name[2] = {
92         "null",
93         "compound",
94 };
95
96 static const char *     nfscltproc4name[35] = {
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 };
105
106 static const char *     nfssrvproc4opname[40] = {
107         "op0-unused",   "op1-unused", "op2-future",  "access",     "close",       "commit",
108         "create",       "delegpurge", "delegreturn", "getattr",    "getfh",       "link",
109         "lock",         "lockt",      "locku",       "lookup",     "lookup_root", "nverify",
110         "open",         "openattr",   "open_conf",   "open_dgrd",  "putfh",       "putpubfh",
111         "putrootfh",    "read",       "readdir",     "readlink",   "remove",      "rename",
112         "renew",        "restorefh",  "savefh",      "secinfo",    "setattr",     "setcltid",
113         "setcltidconf", "verify",     "write",       "rellockowner"
114 };
115
116 typedef struct statinfo {
117         char            *tag;
118         int             nrvals;
119         unsigned int *  valptr;
120 } statinfo;
121
122 /*
123  * We now build the arrays of statinfos using macros, which will make it easier
124  * to add new variables for --diff-stat.
125  * e.g., SRV(net) expands into the struct statinfo:  { "net", 5, srvnetinfo }
126  */
127 #define ARRAYSIZE(x)            sizeof(x)/sizeof(*x)
128 #define STATINFO(k, t, s...)    { #t, ARRAYSIZE(k##t##info##s), k##t##info##s }
129 #define SRV(t, s...)            STATINFO(srv, t, s)
130 #define CLT(t, s...)            STATINFO(clt, t, s)
131 #define DECLARE_SRV(n, s...)    static statinfo n##s[] = { \
132                                         SRV(net,s), \
133                                         SRV(rpc,s), \
134                                         SRV(rc,s), \
135                                         SRV(fh,s), \
136                                         SRV(proc2,s), \
137                                         SRV(proc3,s),\
138                                         SRV(proc4,s), \
139                                         SRV(proc4ops,s),\
140                                         { NULL, 0, NULL }\
141                                 }
142 #define DECLARE_CLT(n, s...)    static statinfo n##s[] = { \
143                                         CLT(net,s), \
144                                         CLT(rpc,s), \
145                                         CLT(proc2,s),\
146                                         CLT(proc3,s), \
147                                         CLT(proc4,s),\
148                                         { NULL, 0, NULL }\
149                                 }
150 DECLARE_SRV(srvinfo);
151 DECLARE_SRV(srvinfo, _old);
152 DECLARE_CLT(cltinfo);
153 DECLARE_CLT(cltinfo, _old);
154
155 static void             print_numbers(const char *, unsigned int *,
156                                         unsigned int);
157 static void             print_callstats(const char *, const char **,
158                                         unsigned int *, unsigned int);
159 static int              parse_statfile(const char *, struct statinfo *);
160
161 static statinfo         *get_stat_info(const char *, struct statinfo *);
162
163 static int              mounts(const char *);
164
165 static void             get_stats(const char *, struct statinfo *, int *, int,
166                                         int);
167 static int              has_stats(const unsigned int *);
168 static void             diff_stats(struct statinfo *, struct statinfo *, int);
169 static void             unpause(int);
170
171 static time_t           starttime;
172
173 #define PRNT_CALLS      0x0001
174 #define PRNT_RPC        0x0002
175 #define PRNT_NET        0x0004
176 #define PRNT_FH         0x0008
177 #define PRNT_RC         0x0010
178 #define PRNT_AUTO       0x1000
179 #define PRNT_V2         0x2000
180 #define PRNT_V3         0x4000
181 #define PRNT_V4         0x8000
182 #define PRNT_ALL        0x0fff
183
184 int versions[] = {
185         PRNT_V2,
186         PRNT_V3,
187         PRNT_V4
188 };
189
190 void usage(char *name)
191 {
192         printf("Usage: %s [OPTION]...\n\
193 \n\
194   -m, --mounted\t\tShow statistics on mounted NFS filesystems\n\
195   -c, --client\t\tShow NFS client statistics\n\
196   -s, --server\t\tShow NFS server statistics\n\
197   -2\t\t\tShow NFS version 2 statistics\n\
198   -3\t\t\tShow NFS version 3 statistics\n\
199   -4\t\t\tShow NFS version 4 statistics\n\
200   -o [facility]\t\tShow statistics on particular facilities.\n\
201      nfs\tNFS protocol information\n\
202      rpc\tGeneral RPC information\n\
203      net\tNetwork layer statistics\n\
204      fh\t\tUsage information on the server's file handle cache\n\
205      rc\t\tUsage information on the server's request reply cache\n\
206      all\tSelect all of the above\n\
207   -v, --verbose, --all\tSame as '-o all'\n\
208   -r, --rpc\t\tShow RPC statistics\n\
209   -n, --nfs\t\tShow NFS statistics\n\
210   -Z, --sleep\tSaves stats, pauses, diffs current and saved\n\
211   --version\t\tShow program version\n\
212   --help\t\tWhat you just did\n\
213 \n", name);
214         exit(0);
215 }
216
217 static struct option longopts[] =
218 {
219         { "acl", 0, 0, 'a' },
220         { "all", 0, 0, 'v' },
221         { "auto", 0, 0, '\3' },
222         { "client", 0, 0, 'c' },
223         { "mounts", 0, 0, 'm' },
224         { "nfs", 0, 0, 'n' },
225         { "rpc", 0, 0, 'r' },
226         { "server", 0, 0, 's' },
227         { "verbose", 0, 0, 'v' },
228         { "zero", 0, 0, 'z' },
229         { "help", 0, 0, '\1' },
230         { "version", 0, 0, '\2' },
231         { "sleep", 0, 0, 'Z' },
232         { NULL, 0, 0, 0 }
233 };
234
235 int
236 main(int argc, char **argv)
237 {
238         int             opt_all = 0,
239                         opt_srv = 0,
240                         opt_clt = 0,
241                         opt_prt = 0,
242                         opt_sleep = 0;
243         int             c;
244         char           *progname;
245
246         struct statinfo *serverinfo = srvinfo,
247                         *serverinfo_tmp = srvinfo_old,
248                         *clientinfo = cltinfo,
249                         *clientinfo_tmp = cltinfo_old;
250
251         struct sigaction act = {
252                 .sa_handler = unpause,
253                 .sa_flags = SA_ONESHOT,
254         };
255
256         if ((progname = strrchr(argv[0], '/')))
257                 progname++;
258         else
259                 progname = argv[0];
260
261         while ((c = getopt_long(argc, argv, "234acmno:Zvrsz\1\2", longopts, NULL)) != EOF) {
262                 switch (c) {
263                 case 'a':
264                         fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
265                         return -1;
266                 case 'c':
267                         opt_clt = 1;
268                         break;
269                 case 'n':
270                         opt_prt |= PRNT_CALLS;
271                         break;
272                 case 'o':
273                         if (!strcmp(optarg, "nfs"))
274                                 opt_prt |= PRNT_CALLS;
275                         else if (!strcmp(optarg, "rpc"))
276                                 opt_prt |= PRNT_RPC;
277                         else if (!strcmp(optarg, "net"))
278                                 opt_prt |= PRNT_NET;
279                         else if (!strcmp(optarg, "rc"))
280                                 opt_prt |= PRNT_RC;
281                         else if (!strcmp(optarg, "fh"))
282                                 opt_prt |= PRNT_FH;
283                         else if (!strcmp(optarg, "all"))
284                                 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
285                         else {
286                                 fprintf(stderr, "nfsstat: unknown category: "
287                                                 "%s\n", optarg);
288                                 return 2;
289                         }
290                         break;
291                 case 'Z':
292                         opt_sleep = 1;
293                         break;
294                 case '2':
295                 case '3':
296                 case '4':
297                         opt_prt |= versions[c - '2'];
298                         break;
299                 case 'v':
300                         opt_all = 1;
301                         break;
302                 case '\3':
303                         opt_prt |= PRNT_AUTO;
304                         break;
305                 case 'r':
306                         opt_prt |= PRNT_RPC;
307                         break;
308                 case 's':
309                         opt_srv = 1;
310                         break;
311                 case 'z':
312                         fprintf(stderr, "nfsstat: zeroing of nfs statistics "
313                                         "not yet supported\n");
314                         return 2;
315                 case 'm':
316                         return mounts(MOUNTSFILE);
317                 case '\1':
318                         usage(progname);
319                         return 0;
320                 case '\2':
321                         fprintf(stdout, "nfsstat: " VERSION "\n");
322                         return 0;
323                 default:
324                         printf("Try `%s --help' for more information.\n", progname);
325                         return -1;
326                 }
327         }
328
329         if (opt_all) {
330                 opt_srv = opt_clt = 1;
331                 opt_prt |= PRNT_ALL;
332         }
333         if (!(opt_srv + opt_clt))
334                 opt_srv = opt_clt = 1;
335         if (!(opt_prt & 0xfff)) {
336                 opt_prt |= PRNT_CALLS + PRNT_RPC;
337         }
338         if (!(opt_prt & 0xe000)) {
339                 opt_prt |= PRNT_AUTO;
340         }
341         if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
342                 fprintf(stderr,
343                         "You requested file handle or request cache "
344                         "statistics while using the -c option.\n"
345                         "This information is available only for the NFS "
346                         "server.\n");
347         }
348
349         if (opt_sleep) {
350                 serverinfo = srvinfo_old;
351                 serverinfo_tmp = srvinfo;
352                 clientinfo = cltinfo_old;
353                 clientinfo_tmp = cltinfo;
354         }
355
356         if (opt_srv)
357                 get_stats(NFSSRVSTAT, serverinfo, &opt_srv, opt_clt, 1);
358         if (opt_clt)
359                 get_stats(NFSCLTSTAT, clientinfo, &opt_clt, opt_srv, 0);
360
361         /* save stat snapshots; wait for signal; then diff current and saved stats */
362         if (opt_sleep) {
363                 starttime = time(NULL);
364                 printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
365                 if (sigaction(SIGINT, &act, NULL) != 0) {
366                         fprintf(stderr, "Error: couldn't register for signal and pause.\n");
367                         return 1;
368                 }
369                 pause();
370                 if (opt_srv) {
371                         get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, 1);
372                         diff_stats(serverinfo_tmp, serverinfo, 1);
373                 }
374                 if (opt_clt) {
375                         get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
376                         diff_stats(clientinfo_tmp, clientinfo, 0);
377                 }
378         }
379
380         if (opt_srv) {
381                 if (opt_prt & PRNT_NET) {
382                         print_numbers(
383                         "Server packet stats:\n"
384                         "packets    udp        tcp        tcpconn\n",
385                         srvnetinfo, 4
386                         );
387                         printf("\n");
388                 }
389                 if (opt_prt & PRNT_RPC) {
390                         print_numbers(
391                         "Server rpc stats:\n"
392                         "calls      badcalls   badauth    badclnt    xdrcall\n",
393                         srvrpcinfo, 5
394                         );
395                         printf("\n");
396                 }
397                 if (opt_prt & PRNT_RC) {
398                         print_numbers(
399                         "Server reply cache:\n"
400                         "hits       misses     nocache\n",
401                         srvrcinfo, 3
402                         );
403                         printf("\n");
404                 }
405
406                 /*
407                  * 2.2 puts all fh-related info after the 'rc' header
408                  * 2.4 puts all fh-related info after the 'fh' header, but relocates
409                  *     'stale' to the start and swaps dir and nondir :-(  
410                  *     We preseve the 2.2 order
411                  */
412                 if (opt_prt & PRNT_FH) {
413                         if (get_stat_info("fh", srvinfo)) {     /* >= 2.4 */
414                                 int t = srvfhinfo[3];
415                                 srvfhinfo[3]=srvfhinfo[4];
416                                 srvfhinfo[4]=t;
417                                 
418                                 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
419                                 
420                                 print_numbers(
421                                         "Server file handle cache:\n"
422                                         "lookup     anon       ncachedir  ncachedir  stale\n",
423                                         srvfhinfo + 1, 5);
424                         } else                                  /* < 2.4 */
425                                 print_numbers(
426                                         "Server file handle cache:\n"
427                                         "lookup     anon       ncachedir  ncachedir  stale\n",
428                                         srvrcinfo + 3, 5);
429                         printf("\n");
430                 }
431                 if (opt_prt & PRNT_CALLS) {
432                         if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info)))
433                                 print_callstats(
434                                 "Server nfs v2:\n",
435                                     nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
436                                 );
437                         if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info)))
438                                 print_callstats(
439                                 "Server nfs v3:\n",
440                                 nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
441                                 );
442                         if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
443                                 print_callstats(
444                                 "Server nfs v4:\n",
445                                 nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)
446                                 );
447                                 print_callstats(
448                                 "Server nfs v4 operations:\n",
449                                 nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)
450                                 );
451                         }
452                 }
453         }
454
455         if (opt_clt) {
456                 if (opt_prt & PRNT_NET) {
457                         print_numbers(
458                         "Client packet stats:\n"
459                         "packets    udp        tcp        tcpconn\n",
460                         cltnetinfo, 4
461                         );
462                         printf("\n");
463                 }
464                 if (opt_prt & PRNT_RPC) {
465                         print_numbers(
466                         "Client rpc stats:\n"
467                         "calls      retrans    authrefrsh\n",
468                         cltrpcinfo, 3
469                         );
470                         printf("\n");
471                 }
472                 if (opt_prt & PRNT_CALLS) {
473                         if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info)))
474                                 print_callstats(
475                                 "Client nfs v2:\n",
476                                 nfsv2name, cltproc2info + 1,  sizeof(nfsv2name)/sizeof(char *)
477                                 );
478                         if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info)))
479                                 print_callstats(
480                                 "Client nfs v3:\n",
481                                 nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
482                                 );
483                         if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info)))
484                                 print_callstats(
485                                 "Client nfs v4:\n",
486                                 nfscltproc4name, cltproc4info + 1,  sizeof(nfscltproc4name)/sizeof(char *)
487                                 );
488                 }
489         }
490
491         return 0;
492 }
493
494 static statinfo *
495 get_stat_info(const char *sp, struct statinfo *statp)
496 {
497         struct statinfo *ip;
498
499         for (ip = statp; ip->tag; ip++) {
500                 if (!strcmp(sp, ip->tag))
501                         return ip;
502         }
503
504         return NULL;
505 }
506
507 static void
508 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
509 {
510         unsigned int    i;
511
512         fputs(hdr, stdout);
513         for (i = 0; i < nr; i++)
514                 printf("%s%-8d", i? "   " : "", info[i]);
515         printf("\n");
516 }
517
518 static void
519 print_callstats(const char *hdr, const char **names,
520                                  unsigned int *info, unsigned int nr)
521 {
522         unsigned long long      total;
523         unsigned long long      pct;
524         int             i, j;
525
526         fputs(hdr, stdout);
527         for (i = 0, total = 0; i < nr; i++)
528                 total += info[i];
529         if (!total)
530                 total = 1;
531         for (i = 0; i < nr; i += 6) {
532                 for (j = 0; j < 6 && i + j < nr; j++)
533                         printf("%-13s", names[i+j]);
534                 printf("\n");
535                 for (j = 0; j < 6 && i + j < nr; j++) {
536                         pct = ((unsigned long long) info[i+j]*100)/total;
537                         printf("%-8d%3llu%% ", info[i+j], pct);
538                 }
539                 printf("\n");
540         }
541         printf("\n");
542 }
543
544
545 static int
546 parse_statfile(const char *name, struct statinfo *statp)
547 {
548         char    buffer[4096], *next;
549         FILE    *fp;
550
551         /* Being unable to read e.g. the nfsd stats file shouldn't
552          * be a fatal error -- it usually means the module isn't loaded.
553          */
554         if ((fp = fopen(name, "r")) == NULL) {
555                 // fprintf(stderr, "Warning: %s: %m\n", name);
556                 return 0;
557         }
558
559         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
560                 struct statinfo *ip;
561                 char            *sp, *line = buffer;
562                 unsigned int    i, cnt;
563                 unsigned int    total = 0;
564
565                 if ((next = strchr(line, '\n')) != NULL)
566                         *next++ = '\0';
567                 if (!(sp = strtok(line, " \t")))
568                         continue;
569
570                 ip = get_stat_info(sp, statp);
571                 if (!ip)
572                         continue;
573
574                 cnt = ip->nrvals;
575
576                 for (i = 0; i < cnt; i++) {
577                         if (!(sp = strtok(NULL, " \t")))
578                                 break;
579                         ip->valptr[i] = atoi(sp);
580                         total += ip->valptr[i];
581                 }
582                 ip->valptr[cnt - 1] = total;
583         }
584
585         fclose(fp);
586         return 1;
587 }
588
589 static int
590 mounts(const char *name)
591 {
592         char    buffer[4096], *next;
593         FILE    *fp;
594
595         /* Being unable to read e.g. the nfsd stats file shouldn't
596          * be a fatal error -- it usually means the module isn't loaded.
597          */
598         if ((fp = fopen(name, "r")) == NULL) {
599                 fprintf(stderr, "Warning: %s: %m\n", name);
600                 return 0;
601         }
602
603         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
604                 char          *line = buffer;
605                 char          *device, *mount, *type, *flags;
606
607                 if ((next = strchr(line, '\n')) != NULL)
608                         *next = '\0';
609
610                 if (!(device = strtok(line, " \t")))
611                         continue;
612
613                 if (!(mount = strtok(NULL, " \t")))
614                         continue;
615
616                 if (!(type = strtok(NULL, " \t")))
617                         continue;
618
619                 if (strcmp(type, "nfs")) {
620                     continue;
621                 }
622
623                 if (!(flags = strtok(NULL, " \t")))
624                         continue;
625
626                 printf("%s from %s\n", mount, device);
627                 printf(" Flags:\t%s\n", flags);
628                 printf("\n");
629
630                 continue;
631         }
632
633         fclose(fp);
634         return 1;
635 }
636
637 static void
638 get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
639                 int is_srv)
640 {
641         const char *label = is_srv ? "Server" : "Client";
642
643         if (!parse_statfile(file, info)) {
644                 if (!other_opt) {
645                         fprintf(stderr, "Warning: No %s Stats (%s: %m). \n", label, file);
646                         exit(2);
647                 }
648                 *opt = 0;
649         }
650 }
651
652 /*
653  * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
654  * denotes the number of subsequent entries.  statinfo value arrays contain an additional
655  * field at the end which contains the sum of all previous elements in the array -- so,
656  * there are stats if the sum's greater than the entry-count.
657  */
658 static int
659 has_stats(const unsigned int *info)
660 {
661         return (info[0] && info[info[0] + 1] > info[0]);
662 }
663
664 /*
665  * take the difference of each individual stat value in 'new' and 'old'
666  * and store the results back into 'new'
667  */
668 static void
669 diff_stats(struct statinfo *new, struct statinfo *old, int is_srv)
670 {
671         int i, j, nodiff_first_index, should_diff;
672
673         /*
674          * Different stat types have different formats in the /proc
675          * files: for the proc2/3/4-type stats, the first entry has
676          * the total number of subsequent entries; one does not want
677          * to diff that first entry.  The other stat types aren't like
678          * this.  So, we diff a given entry if it's not of one of the
679          * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
680          * the first entry ("j" > 0).
681          */
682         nodiff_first_index = 2 + (2 * is_srv);
683
684         for (i = 0; old[i].tag; i++) {
685                 for (j = 0; j < new[i].nrvals; j++) {
686                         should_diff = (i < nodiff_first_index || j > 0);
687                         if (should_diff)
688                                 new[i].valptr[j] -= old[i].valptr[j];
689                 }
690
691                 /*
692                  * Make sure that the "totals" entry (last value in
693                  * each stat array) for the procX-type stats has the
694                  * "numentries" entry's (first value in procX-type
695                  * stat arrays) constant value added-back after the
696                  * diff -- i.e., it should always be included in the
697                  * total.
698                  */
699                 if (!strncmp("proc", new[i].tag, 4))
700                         new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
701         }
702 }
703
704 static void
705 unpause(int sig)
706 {
707         double time_diff;
708         int minutes, seconds;
709         time_t endtime;
710
711         endtime = time(NULL);
712         time_diff = difftime(endtime, starttime);
713         minutes = time_diff / 60;
714         seconds = (int)time_diff % 60;
715         printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds);
716 }