]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/nfsstat/nfsstat.c
afd323ede6f1f269fba68e6d9ecf998cf8c78229
[nfs-utils.git] / utils / nfsstat / nfsstat.c
1 /*
2  * nfsstat.c            Output NFS statistics
3  *
4  * Copyright (C) 1995, 1996, 1999 Olaf Kirch <okir@monad.swb.de>
5  */
6
7 #include "config.h"
8
9 #define NFSSVCSTAT      "/proc/net/rpc/nfsd"
10 #define NFSCLTSTAT      "/proc/net/rpc/nfs"
11
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <fcntl.h>
17 #include <errno.h>
18
19 #define MAXNRVALS       32
20
21 static unsigned int     svcv2info[19];  /* NFSv2 call counts ([0] == 18) */
22 static unsigned int     cltv2info[19];  /* NFSv2 call counts ([0] == 18) */
23 static unsigned int     svcv3info[23];  /* NFSv3 call counts ([0] == 22) */
24 static unsigned int     cltv3info[23];  /* NFSv3 call counts ([0] == 22) */
25 static unsigned int     svcnetinfo[4];  /* 0  # of received packets
26                                          * 1  UDP packets
27                                          * 2  TCP packets
28                                          * 3  TCP connections
29                                          */
30 static unsigned int     cltnetinfo[4];  /* 0  # of received packets
31                                          * 1  UDP packets
32                                          * 2  TCP packets
33                                          * 3  TCP connections
34                                          */
35
36 static unsigned int     svcrpcinfo[5];  /* 0  total # of RPC calls
37                                          * 1  total # of bad calls
38                                          * 2  bad format
39                                          * 3  authentication failed
40                                          * 4  unknown client
41                                          */
42 static unsigned int     cltrpcinfo[3];  /* 0  total # of RPC calls
43                                          * 1  retransmitted calls
44                                          * 2  cred refreshs
45                                          */
46
47 static unsigned int     svcrcinfo[8];   /* 0  repcache hits
48                                          * 1  repcache hits
49                                          * 2  uncached reqs
50                                          * (for pre-2.4 kernels:)
51                                          * 3  FH lookups
52                                          * 4  'anon' FHs
53                                          * 5  noncached non-directories
54                                          * 6  noncached directories
55                                          * 7  stale
56                                          */
57
58 static unsigned int     svcfhinfo[6];   /* (for kernels >= 2.4.0)
59                                          * 0  stale
60                                          * 1  FH lookups
61                                          * 2  'anon' FHs
62                                          * 3  noncached non-directories
63                                          * 4  noncached directories
64                                          * leave hole to relocate stale for order
65                                          *    compatability.
66                                          */
67
68 static const char *     nfsv2name[18] = {
69         "null", "getattr", "setattr", "root",   "lookup",  "readlink",
70         "read", "wrcache", "write",   "create", "remove",  "rename",
71         "link", "symlink", "mkdir",   "rmdir",  "readdir", "fsstat"
72 };
73
74 static const char *     nfsv3name[22] = {
75         "null",   "getattr", "setattr",  "lookup", "access",  "readlink",
76         "read",   "write",   "create",   "mkdir",  "symlink", "mknod",
77         "remove", "rmdir",   "rename",   "link",   "readdir", "readdirplus",
78         "fsstat", "fsinfo",  "pathconf", "commit"
79 };
80
81 typedef struct statinfo {
82         char            *tag;
83         int             nrvals;
84         unsigned int *  valptr;
85 } statinfo;
86
87 static statinfo         svcinfo[] = {
88         { "net",        4,      svcnetinfo      },
89         { "rpc",        5,      svcrpcinfo      },
90         { "rc",         8,      svcrcinfo       },
91         { "fh",         5,      svcfhinfo       },
92         { "proc2",      19,     svcv2info       },
93         { "proc3",      23,     svcv3info       },
94         { NULL,         0,      0               }
95 };
96
97 static statinfo         cltinfo[] = {
98         { "net",        4,      cltnetinfo      },
99         { "rpc",        3,      cltrpcinfo      },
100         { "proc2",      19,     cltv2info       },
101         { "proc3",      23,     cltv3info       },
102         { NULL,         0,      0               }
103 };
104
105 static void             print_numbers(const char *, unsigned int *,
106                                         unsigned int);
107 static void             print_callstats(const char *, const char **,
108                                         unsigned int *, unsigned int);
109 static int              parse_statfile(const char *, struct statinfo *);
110
111 static statinfo         *get_stat_info(const char *, struct statinfo *);
112
113
114 #define PRNT_CALLS      0x0001
115 #define PRNT_RPC        0x0002
116 #define PRNT_NET        0x0004
117 #define PRNT_FH         0x0008
118 #define PRNT_RC         0x0010
119 #define PRNT_ALL        0xffff
120
121 int
122 main(int argc, char **argv)
123 {
124         int             opt_all = 0,
125                         opt_srv = 0,
126                         opt_clt = 0,
127                         opt_prt = 0;
128         int             c;
129
130         while ((c = getopt(argc, argv, "acno:rsz")) != -1) {
131                 switch (c) {
132                 case 'a':
133                         opt_all = 1;
134                         break;
135                 case 'c':
136                         opt_clt = 1;
137                         break;
138                 case 'n':
139                         opt_prt |= PRNT_CALLS;
140                         break;
141                 case 'o':
142                         if (!strcmp(optarg, "nfs"))
143                                 opt_prt |= PRNT_CALLS;
144                         else if (!strcmp(optarg, "rpc"))
145                                 opt_prt |= PRNT_RPC;
146                         else if (!strcmp(optarg, "net"))
147                                 opt_prt |= PRNT_NET;
148                         else if (!strcmp(optarg, "rc"))
149                                 opt_prt |= PRNT_RC;
150                         else if (!strcmp(optarg, "fh"))
151                                 opt_prt |= PRNT_FH;
152                         else {
153                                 fprintf(stderr, "nfsstat: unknown category: "
154                                                 "%s\n", optarg);
155                                 return 2;
156                         }
157                         break;
158                 case 'r':
159                         opt_prt |= PRNT_RPC;
160                         break;
161                 case 's':
162                         opt_srv = 1;
163                         break;
164                 case 'z':
165                         fprintf(stderr, "nfsstat: zeroing of nfs statistics "
166                                         "not yet supported\n");
167                         return 2;
168                 }
169         }
170
171         if (opt_all) {
172                 opt_srv = opt_clt = 1;
173                 opt_prt = PRNT_ALL;
174         }
175         if (!(opt_srv + opt_clt))
176                 opt_srv = opt_clt = 1;
177         if (!opt_prt)
178                 opt_prt = PRNT_CALLS + PRNT_RPC;
179         if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
180                 fprintf(stderr,
181                         "You requested file handle or request cache "
182                         "statistics while using the -c option.\n"
183                         "This information is available only for the NFS "
184                         "server.\n");
185         }
186
187         if ((opt_srv && !parse_statfile(NFSSVCSTAT, svcinfo))
188          || (opt_clt && !parse_statfile(NFSCLTSTAT, cltinfo)))
189                 return 2;
190
191         if (opt_srv) {
192                 if (opt_prt & PRNT_NET) {
193                         print_numbers(
194                         "Server packet stats:\n"
195                         "packets    udp        tcp        tcpconn\n",
196                         svcnetinfo, 4
197                         );
198                 }
199                 if (opt_prt & PRNT_RPC) {
200                         print_numbers(
201                         "Server rpc stats:\n"
202                         "calls      badcalls   badauth    badclnt    xdrcall\n",
203                         svcrpcinfo, 5
204                         );
205                 }
206                 if (opt_prt & PRNT_RC) {
207                         print_numbers(
208                         "Server reply cache:\n"
209                         "hits       misses     nocache\n",
210                         svcrcinfo, 3
211                         );
212                 }
213
214                 /*
215                  * 2.2 puts all fh-related info after the 'rc' header
216                  * 2.4 puts all fh-related info after the 'fh' header, but relocates
217                  *     'stale' to the start :-(  We keep it at the end.
218                  */
219                 if (opt_prt & PRNT_FH) {
220                         if (get_stat_info("fh", svcinfo)) {     /* >= 2.4 */
221                                 svcfhinfo[5]=svcfhinfo[0]; /* relocate 'stale' */
222                                 print_numbers(
223                                         "Server file handle cache:\n"
224                                         "lookup     anon       ncachedir ncachedir  stale\n",
225                                         svcfhinfo + 1, 5);
226                         } else                                  /* < 2.4 */
227                                 print_numbers(
228                                         "Server file handle cache:\n"
229                                         "lookup     anon       ncachedir ncachedir  stale\n",
230                                         svcrcinfo + 3, 5);
231                 }
232                 if (opt_prt & PRNT_CALLS) {
233                         print_callstats(
234                         "Server nfs v2:\n",
235                         nfsv2name, svcv2info + 1, 18
236                         );
237                         if (svcv3info[0])
238                                 print_callstats(
239                                 "Server nfs v3:\n",
240                                 nfsv3name, svcv3info + 1, 22
241                                 );
242                 }
243         }
244
245         if (opt_clt) {
246                 if (opt_prt & PRNT_NET) {
247                         print_numbers(
248                         "Client packet stats:\n"
249                         "packets    udp        tcp        tcpconn\n",
250                         cltnetinfo, 4
251                         );
252                 }
253                 if (opt_prt & PRNT_RPC) {
254                         print_numbers(
255                         "Client rpc stats:\n"
256                         "calls      retrans    authrefrsh\n",
257                         cltrpcinfo, 3
258                         );
259                 }
260                 if (opt_prt & PRNT_CALLS) {
261                         print_callstats(
262                         "Client nfs v2:\n",
263                         nfsv2name, cltv2info + 1, 18
264                         );
265                         if (cltv3info[0])
266                                 print_callstats(
267                                 "Client nfs v3:\n",
268                                 nfsv3name, cltv3info + 1, 22
269                                 );
270                 }
271         }
272
273         return 0;
274 }
275
276 static statinfo *
277 get_stat_info(const char *sp, struct statinfo *statp)
278 {
279         struct statinfo *ip;
280
281         for (ip = statp; ip->tag; ip++) {
282                 if (!strcmp(sp, ip->tag))
283                         return ip;
284         }
285
286         return NULL;
287 }
288
289 static void
290 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
291 {
292         unsigned int    i;
293
294         fputs(hdr, stdout);
295         for (i = 0; i < nr; i++)
296                 printf("%s%-8d", i? "   " : "", info[i]);
297         printf("\n");
298 }
299
300 static void
301 print_callstats(const char *hdr, const char **names,
302                                  unsigned int *info, unsigned int nr)
303 {
304         unsigned int    total;
305         int             i, j;
306
307         fputs(hdr, stdout);
308         for (i = 0, total = 0; i < nr; i++)
309                 total += info[i];
310         if (!total)
311                 total = 1;
312         for (i = 0; i < nr; i += 6) {
313                 for (j = 0; j < 6 && i + j < nr; j++)
314                         printf("%-11s", names[i+j]);
315                 printf("\n");
316                 for (j = 0; j < 6 && i + j < nr; j++)
317                         printf("%-6d %2d%% ",
318                                 info[i+j], 100 * info[i+j] / total);
319                 printf("\n");
320         }
321         printf("\n");
322 }
323
324
325 static int
326 parse_statfile(const char *name, struct statinfo *statp)
327 {
328         char    buffer[4096], *next;
329         FILE    *fp;
330
331         /* Being unable to read e.g. the nfsd stats file shouldn't
332          * be a fatal error -- it usually means the module isn't loaded.
333          */
334         if ((fp = fopen(name, "r")) == NULL) {
335                 fprintf(stderr, "Warning: %s: %m\n", name);
336                 return 1;
337         }
338
339         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
340                 struct statinfo *ip;
341                 char            *sp, *line = buffer;
342                 int             i, cnt;
343
344                 if ((next = strchr(line, '\n')) != NULL)
345                         *next++ = '\0';
346                 if (!(sp = strtok(line, " \t")))
347                         continue;
348
349                 ip = get_stat_info(sp, statp);
350                 if (!ip)
351                         continue;
352
353                 cnt = ip->nrvals;
354
355                 for (i = 0; i < cnt; i++) {
356                         if (!(sp = strtok(NULL, " \t")))
357                                 break;
358                         ip->valptr[i] = atoi(sp);
359                 }
360         }
361
362         fclose(fp);
363         return 1;
364 }