bb1d5ad70e16264a964858f537050d0c88d24bd8
[openafs.git] / src / xstat / xstat_cm_test.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Description:
12  *      Test of the xstat_cm module.
13  *
14  *------------------------------------------------------------------------*/
15
16 #include <afsconfig.h>
17 #include <afs/param.h>
18
19 #include <roken.h>
20
21 #include "xstat_cm.h"           /*Interface for xstat_cm module */
22 #include <afs/cmd.h>            /*Command line interpreter */
23 #include <afs/afsutil.h>
24
25 /*
26  * Command line parameter indices.
27  *      P_CM_NAMES : List of CacheManager names.
28  *      P_COLL_IDS : List of collection IDs to pick up.
29  *      P_ONESHOT  : Are we gathering exactly one round of data?
30  *      P_DEBUG    : Enable debugging output?
31  */
32 #define P_CM_NAMES      0
33 #define P_COLL_IDS      1
34 #define P_ONESHOT       2
35 #define P_FREQUENCY     3
36 #define P_PERIOD        4
37 #define P_DEBUG         5
38
39 /*
40  * Private globals.
41  */
42 static int debugging_on = 0;    /*Are we debugging? */
43 static int one_shot = 0;        /*Single round of data collection? */
44
45 static char *fsOpNames[] = {
46     "FetchData",
47     "FetchACL",
48     "FetchStatus",
49     "StoreData",
50     "StoreACL",
51     "StoreStatus",
52     "RemoveFile",
53     "CreateFile",
54     "Rename",
55     "Symlink",
56     "Link",
57     "MakeDir",
58     "RemoveDir",
59     "SetLock",
60     "ExtendLock",
61     "ReleaseLock",
62     "GetStatistics",
63     "GiveUpCallbacks",
64     "GetVolumeInfo",
65     "GetVolumeStatus",
66     "SetVolumeStatus",
67     "GetRootVolume",
68     "CheckToken",
69     "GetTime",
70     "NGetVolumeInfo",
71     "BulkStatus",
72     "XStatsVersion",
73     "GetXStats",
74     "XLookup"
75 };
76
77 static char *cmOpNames[] = {
78     "CallBack",
79     "InitCallBackState",
80     "Probe",
81     "GetLock",
82     "GetCE",
83     "XStatsVersion",
84     "GetXStats"
85 };
86
87 static char *xferOpNames[] = {
88     "FetchData",
89     "StoreData"
90 };
91
92
93 /* Print detailed functional call statistics */
94
95 void
96 print_cmCallStats(void)
97 {
98     char *printableTime;        /*Ptr to printable time string */
99     afs_int32 nitems;
100     struct afs_CMStats *cmp;
101     time_t probeTime = xstat_cm_Results.probeTime;
102
103     printableTime = ctime(&probeTime);
104     printableTime[strlen(printableTime) - 1] = '\0';
105
106     printf
107         ("AFSCB_XSTATSCOLL_CALL_INFO (coll %d) for CM %s\n[Probe %u, %s]\n\n",
108          xstat_cm_Results.collectionNumber, xstat_cm_Results.connP->hostName,
109          xstat_cm_Results.probeNum, printableTime);
110
111     cmp = (struct afs_CMStats *)(xstat_cm_Results.data.AFSCB_CollData_val);
112     nitems = xstat_cm_Results.data.AFSCB_CollData_len;
113
114 #define AFS_CS(call) \
115     if (nitems > 0) { \
116         printf("\t%10u %s\n", cmp->callInfo.C_ ## call, #call); \
117         nitems--; \
118     }
119
120     AFS_CM_CALL_STATS
121 #undef AFS_CS
122 }
123
124
125 /*------------------------------------------------------------------------
126  * PrintUpDownStats
127  *
128  * Description:
129  *      Print the up/downtime stats for the given class of server records
130  *      provided.
131  *
132  * Arguments:
133  *      a_upDownP : Ptr to the server up/down info.
134  *
135  * Returns:
136  *      Nothing.
137  *
138  * Environment:
139  *      Nothing interesting.
140  *
141  * Side Effects:
142  *      As advertised.
143  *------------------------------------------------------------------------*/
144
145 void
146 PrintUpDownStats(struct afs_stats_SrvUpDownInfo *a_upDownP)
147 {                               /*PrintUpDownStats */
148
149     /*
150      * First, print the simple values.
151      */
152     printf("\t\t%10u numTtlRecords\n", a_upDownP->numTtlRecords);
153     printf("\t\t%10u numUpRecords\n", a_upDownP->numUpRecords);
154     printf("\t\t%10u numDownRecords\n", a_upDownP->numDownRecords);
155     printf("\t\t%10u sumOfRecordAges\n", a_upDownP->sumOfRecordAges);
156     printf("\t\t%10u ageOfYoungestRecord\n", a_upDownP->ageOfYoungestRecord);
157     printf("\t\t%10u ageOfOldestRecord\n", a_upDownP->ageOfOldestRecord);
158     printf("\t\t%10u numDowntimeIncidents\n",
159            a_upDownP->numDowntimeIncidents);
160     printf("\t\t%10u numRecordsNeverDown\n", a_upDownP->numRecordsNeverDown);
161     printf("\t\t%10u maxDowntimesInARecord\n",
162            a_upDownP->maxDowntimesInARecord);
163     printf("\t\t%10u sumOfDowntimes\n", a_upDownP->sumOfDowntimes);
164     printf("\t\t%10u shortestDowntime\n", a_upDownP->shortestDowntime);
165     printf("\t\t%10u longestDowntime\n", a_upDownP->longestDowntime);
166
167     /*
168      * Now, print the array values.
169      */
170     printf("\t\tDowntime duration distribution:\n");
171     printf("\t\t\t%8u: 0 min .. 10 min\n", a_upDownP->downDurations[0]);
172     printf("\t\t\t%8u: 10 min .. 30 min\n", a_upDownP->downDurations[1]);
173     printf("\t\t\t%8u: 30 min .. 1 hr\n", a_upDownP->downDurations[2]);
174     printf("\t\t\t%8u: 1 hr .. 2 hr\n", a_upDownP->downDurations[3]);
175     printf("\t\t\t%8u: 2 hr .. 4 hr\n", a_upDownP->downDurations[4]);
176     printf("\t\t\t%8u: 4 hr .. 8 hr\n", a_upDownP->downDurations[5]);
177     printf("\t\t\t%8u: > 8 hr\n", a_upDownP->downDurations[6]);
178
179     printf("\t\tDowntime incident distribution:\n");
180     printf("\t\t\t%8u: 0 times\n", a_upDownP->downIncidents[0]);
181     printf("\t\t\t%8u: 1 time\n", a_upDownP->downIncidents[1]);
182     printf("\t\t\t%8u: 2 .. 5 times\n", a_upDownP->downIncidents[2]);
183     printf("\t\t\t%8u: 6 .. 10 times\n", a_upDownP->downIncidents[3]);
184     printf("\t\t\t%8u: 10 .. 50 times\n", a_upDownP->downIncidents[4]);
185     printf("\t\t\t%8u: > 50 times\n", a_upDownP->downIncidents[5]);
186
187 }                               /*PrintUpDownStats */
188
189
190 /*------------------------------------------------------------------------
191  * PrintOverallPerfInfo
192  *
193  * Description:
194  *      Print out overall performance numbers.
195  *
196  * Arguments:
197  *      a_ovP : Ptr to the overall performance numbers.
198  *
199  * Returns:
200  *      Nothing.
201  *
202  * Environment:
203  *      All the info we need is nestled into xstat_cm_Results.
204  *
205  * Side Effects:
206  *      As advertised.
207  *------------------------------------------------------------------------*/
208
209 void
210 PrintOverallPerfInfo(struct afs_stats_CMPerf *a_ovP)
211 {                               /*PrintOverallPerfInfo */
212
213     printf("\t%10u numPerfCalls\n", a_ovP->numPerfCalls);
214
215     printf("\t%10u epoch\n", a_ovP->epoch);
216     printf("\t%10u numCellsVisible\n", a_ovP->numCellsVisible);
217     printf("\t%10u numCellsContacted\n", a_ovP->numCellsContacted);
218     printf("\t%10u dlocalAccesses\n", a_ovP->dlocalAccesses);
219     printf("\t%10u vlocalAccesses\n", a_ovP->vlocalAccesses);
220     printf("\t%10u dremoteAccesses\n", a_ovP->dremoteAccesses);
221     printf("\t%10u vremoteAccesses\n", a_ovP->vremoteAccesses);
222     printf("\t%10u cacheNumEntries\n", a_ovP->cacheNumEntries);
223     printf("\t%10u cacheBlocksTotal\n", a_ovP->cacheBlocksTotal);
224     printf("\t%10u cacheBlocksInUse\n", a_ovP->cacheBlocksInUse);
225     printf("\t%10u cacheBlocksOrig\n", a_ovP->cacheBlocksOrig);
226     printf("\t%10u cacheMaxDirtyChunks\n", a_ovP->cacheMaxDirtyChunks);
227     printf("\t%10u cacheCurrDirtyChunks\n", a_ovP->cacheCurrDirtyChunks);
228     printf("\t%10u dcacheHits\n", a_ovP->dcacheHits);
229     printf("\t%10u vcacheHits\n", a_ovP->vcacheHits);
230     printf("\t%10u dcacheMisses\n", a_ovP->dcacheMisses);
231     printf("\t%10u vcacheMisses\n", a_ovP->vcacheMisses);
232     printf("\t%10u cacheFilesReused\n", a_ovP->cacheFilesReused);
233     printf("\t%10u vcacheXAllocs\n", a_ovP->vcacheXAllocs);
234     printf("\t%10u dcacheXAllocs\n", a_ovP->dcacheXAllocs);
235
236     printf("\t%10u bufAlloced\n", a_ovP->bufAlloced);
237     printf("\t%10u bufHits\n", a_ovP->bufHits);
238     printf("\t%10u bufMisses\n", a_ovP->bufMisses);
239     printf("\t%10u bufFlushDirty\n", a_ovP->bufFlushDirty);
240
241     printf("\t%10u LargeBlocksActive\n", a_ovP->LargeBlocksActive);
242     printf("\t%10u LargeBlocksAlloced\n", a_ovP->LargeBlocksAlloced);
243     printf("\t%10u SmallBlocksActive\n", a_ovP->SmallBlocksActive);
244     printf("\t%10u SmallBlocksAlloced\n", a_ovP->SmallBlocksAlloced);
245     printf("\t%10u OutStandingMemUsage\n", a_ovP->OutStandingMemUsage);
246     printf("\t%10u OutStandingAllocs\n", a_ovP->OutStandingAllocs);
247     printf("\t%10u CallBackAlloced\n", a_ovP->CallBackAlloced);
248     printf("\t%10u CallBackFlushes\n", a_ovP->CallBackFlushes);
249     printf("\t%10u CallBackLoops\n", a_ovP->cbloops);
250
251     printf("\t%10u srvRecords\n", a_ovP->srvRecords);
252     printf("\t%10u srvNumBuckets\n", a_ovP->srvNumBuckets);
253     printf("\t%10u srvMaxChainLength\n", a_ovP->srvMaxChainLength);
254     printf("\t%10u srvMaxChainLengthHWM\n", a_ovP->srvMaxChainLengthHWM);
255     printf("\t%10u srvRecordsHWM\n", a_ovP->srvRecordsHWM);
256
257     printf("\t%10u cacheBucket0_Discarded\n",  a_ovP->cacheBucket0_Discarded);
258     printf("\t%10u cacheBucket1_Discarded\n",  a_ovP->cacheBucket1_Discarded);
259     printf("\t%10u cacheBucket2_Discarded\n",  a_ovP->cacheBucket2_Discarded);
260
261     printf("\t%10u sysName_ID\n", a_ovP->sysName_ID);
262
263     printf("\tFile Server up/downtimes, same cell:\n");
264     PrintUpDownStats(&(a_ovP->fs_UpDown[0]));
265
266     printf("\tFile Server up/downtimes, diff cell:\n");
267     PrintUpDownStats(&(a_ovP->fs_UpDown[1]));
268
269     printf("\tVL Server up/downtimes, same cell:\n");
270     PrintUpDownStats(&(a_ovP->vl_UpDown[0]));
271
272     printf("\tVL Server up/downtimes, diff cell:\n");
273     PrintUpDownStats(&(a_ovP->vl_UpDown[1]));
274
275 }                               /*PrintOverallPerfInfo */
276
277
278 /*------------------------------------------------------------------------
279  * PrintPerfInfo
280  *
281  * Description:
282  *      Print out the AFSCB_XSTATSCOLL_PERF_INFO collection we just
283  *      received.
284  *
285  * Arguments:
286  *      None.
287  *
288  * Returns:
289  *      Nothing.
290  *
291  * Environment:
292  *      All the info we need is nestled into xstat_cm_Results.
293  *
294  * Side Effects:
295  *      As advertised.
296  *------------------------------------------------------------------------*/
297
298 void
299 PrintPerfInfo(void)
300 {                               /*PrintPerfInfo */
301
302     static afs_int32 perfInt32s = (sizeof(struct afs_stats_CMPerf) >> 2);       /*Correct # int32s to rcv */
303     afs_int32 numInt32s;        /*# int32words received */
304     struct afs_stats_CMPerf *perfP;     /*Ptr to performance stats */
305     char *printableTime;        /*Ptr to printable time string */
306     time_t probeTime = xstat_cm_Results.probeTime;
307
308     numInt32s = xstat_cm_Results.data.AFSCB_CollData_len;
309     if (numInt32s != perfInt32s) {
310         printf("** Data size mismatch in performance collection!");
311         printf("** Expecting %u, got %u\n", perfInt32s, numInt32s);
312         printf("** Version mismatch with Cache Manager\n");
313         return;
314     }
315
316     printableTime = ctime(&probeTime);
317     printableTime[strlen(printableTime) - 1] = '\0';
318     perfP = (struct afs_stats_CMPerf *)
319         (xstat_cm_Results.data.AFSCB_CollData_val);
320
321     printf
322         ("AFSCB_XSTATSCOLL_PERF_INFO (coll %d) for CM %s\n[Probe %u, %s]\n\n",
323          xstat_cm_Results.collectionNumber, xstat_cm_Results.connP->hostName,
324          xstat_cm_Results.probeNum, printableTime);
325
326     PrintOverallPerfInfo(perfP);
327
328 }                               /*PrintPerfInfo */
329
330
331 /*------------------------------------------------------------------------
332  * PrintOpTiming
333  *
334  * Description:
335  *      Print out the contents of an FS RPC op timing structure.
336  *
337  * Arguments:
338  *      a_opIdx   : Index of the AFS operation we're printing number on.
339  *      a_opNames : Ptr to table of operaton names.
340  *      a_opTimeP : Ptr to the op timing structure to print.
341  *
342  * Returns:
343  *      Nothing.
344  *
345  * Environment:
346  *      Nothing interesting.
347  *
348  * Side Effects:
349  *      As advertised.
350  *------------------------------------------------------------------------*/
351
352 void
353 PrintOpTiming(int a_opIdx, char *a_opNames[],
354               struct afs_stats_opTimingData *a_opTimeP)
355 {                               /*PrintOpTiming */
356
357     printf
358         ("%15s: %u ops (%u OK); sum=%lu.%06lu, sqr=%lu.%06lu, min=%lu.%06lu, max=%lu.%06lu\n",
359          a_opNames[a_opIdx], a_opTimeP->numOps, a_opTimeP->numSuccesses,
360          (long)a_opTimeP->sumTime.tv_sec, (long)a_opTimeP->sumTime.tv_usec,
361          (long)a_opTimeP->sqrTime.tv_sec, (long)a_opTimeP->sqrTime.tv_usec,
362          (long)a_opTimeP->minTime.tv_sec, (long)a_opTimeP->minTime.tv_usec,
363          (long)a_opTimeP->maxTime.tv_sec, (long)a_opTimeP->maxTime.tv_usec);
364
365 }                               /*PrintOpTiming */
366
367
368 /*------------------------------------------------------------------------
369  * PrintXferTiming
370  *
371  * Description:
372  *      Print out the contents of a data transfer structure.
373  *
374  * Arguments:
375  *      a_opIdx : Index of the AFS operation we're printing number on.
376  *      a_opNames : Ptr to table of operation names.
377  *      a_xferP : Ptr to the data transfer structure to print.
378  *
379  * Returns:
380  *      Nothing.
381  *
382  * Environment:
383  *      Nothing interesting.
384  *
385  * Side Effects:
386  *      As advertised.
387  *------------------------------------------------------------------------*/
388
389 void
390 PrintXferTiming(int a_opIdx, char *a_opNames[],
391                 struct afs_stats_xferData *a_xferP)
392 {                               /*PrintXferTiming */
393
394     printf
395         ("%s: %u xfers (%u OK), time sum=%lu.%06lu, sqr=%lu.%06lu, min=%lu.%06lu, max=%lu.%06lu\n",
396          a_opNames[a_opIdx], a_xferP->numXfers, a_xferP->numSuccesses,
397          (long)a_xferP->sumTime.tv_sec, (long)a_xferP->sumTime.tv_usec,
398          (long)a_xferP->sqrTime.tv_sec, (long)a_xferP->sqrTime.tv_usec,
399          (long)a_xferP->minTime.tv_sec, (long)a_xferP->minTime.tv_usec,
400          (long)a_xferP->maxTime.tv_sec, (long)a_xferP->maxTime.tv_usec);
401     printf("\t[bytes: sum=%u, min=%u, max=%u]\n", a_xferP->sumBytes,
402            a_xferP->minBytes, a_xferP->maxBytes);
403     printf
404         ("\t[buckets: 0: %u, 1: %u, 2: %u, 3: %u, 4: %u, 5: %u, 6: %u, 7: %u, 8: %u]\n",
405          a_xferP->count[0], a_xferP->count[1], a_xferP->count[2],
406          a_xferP->count[3], a_xferP->count[4], a_xferP->count[5],
407          a_xferP->count[6], a_xferP->count[7], a_xferP->count[8]);
408
409
410 }                               /*PrintXferTiming */
411
412
413 /*------------------------------------------------------------------------
414  * PrintErrInfo
415  *
416  * Description:
417  *      Print out the contents of an FS RPC error info structure.
418  *
419  * Arguments:
420  *      a_opIdx   : Index of the AFS operation we're printing.
421  *      a_opNames : Ptr to table of operation names.
422  *      a_opErrP  : Ptr to the op timing structure to print.
423  *
424  * Returns:
425  *      Nothing.
426  *
427  * Environment:
428  *      Nothing interesting.
429  *
430  * Side Effects:
431  *      As advertised.
432  *------------------------------------------------------------------------*/
433
434 void
435 PrintErrInfo(int a_opIdx, char *a_opNames[],
436              struct afs_stats_RPCErrors *a_opErrP)
437 {                               /*PrintErrInfo */
438
439     printf
440         ("%15s: %u server, %u network, %u prot, %u vol, %u busies, %u other\n",
441          a_opNames[a_opIdx], a_opErrP->err_Server, a_opErrP->err_Network,
442          a_opErrP->err_Protection, a_opErrP->err_Volume,
443          a_opErrP->err_VolumeBusies, a_opErrP->err_Other);
444
445 }                               /*PrintErrInfo */
446
447
448 /*------------------------------------------------------------------------
449  * PrintRPCPerfInfo
450  *
451  * Description:
452  *      Print out a set of RPC performance numbers.
453  *
454  * Arguments:
455  *      a_rpcP : Ptr to RPC perf numbers to print.
456  *
457  * Returns:
458  *      Nothing.
459  *
460  * Environment:
461  *      Nothing interesting.
462  *
463  * Side Effects:
464  *      As advertised.
465  *------------------------------------------------------------------------*/
466
467 void
468 PrintRPCPerfInfo(struct afs_stats_RPCOpInfo *a_rpcP)
469 {                               /*PrintRPCPerfInfo */
470
471     int currIdx;                /*Loop variable */
472
473     /*
474      * Print the contents of each of the opcode-related arrays.
475      */
476     printf("FS Operation Timings:\n---------------------\n");
477     for (currIdx = 0; currIdx < AFS_STATS_NUM_FS_RPC_OPS; currIdx++)
478         PrintOpTiming(currIdx, fsOpNames, &(a_rpcP->fsRPCTimes[currIdx]));
479
480     printf("\nError Info:\n-----------\n");
481     for (currIdx = 0; currIdx < AFS_STATS_NUM_FS_RPC_OPS; currIdx++)
482         PrintErrInfo(currIdx, fsOpNames, &(a_rpcP->fsRPCErrors[currIdx]));
483
484     printf("\nTransfer timings:\n-----------------\n");
485     for (currIdx = 0; currIdx < AFS_STATS_NUM_FS_XFER_OPS; currIdx++)
486         PrintXferTiming(currIdx, xferOpNames,
487                         &(a_rpcP->fsXferTimes[currIdx]));
488
489     printf("\nCM Operation Timings:\n---------------------\n");
490     for (currIdx = 0; currIdx < AFS_STATS_NUM_CM_RPC_OPS; currIdx++)
491         PrintOpTiming(currIdx, cmOpNames, &(a_rpcP->cmRPCTimes[currIdx]));
492
493 }                               /*PrintRPCPerfInfo */
494
495
496 /*------------------------------------------------------------------------
497  * PrintFullPerfInfo
498  *
499  * Description:
500  *      Print out a set of full performance numbers.
501  *
502  * Arguments:
503  *      None.
504  *
505  * Returns:
506  *      Nothing.
507  *
508  * Environment:
509  *      Nothing interesting.
510  *
511  * Side Effects:
512  *      As advertised.
513  *------------------------------------------------------------------------*/
514
515 void
516 PrintFullPerfInfo(void)
517 {                               /*PrintFullPerfInfo */
518
519     struct afs_stats_AuthentInfo *authentP;     /*Ptr to authentication stats */
520     struct afs_stats_AccessInfo *accessinfP;    /*Ptr to access stats */
521     static afs_int32 fullPerfInt32s = (sizeof(struct afs_stats_CMFullPerf) >> 2);       /*Correct #int32s */
522     afs_int32 numInt32s;        /*# int32s actually received */
523     struct afs_stats_CMFullPerf *fullP; /*Ptr to full perf info */
524
525     char *printableTime;        /*Ptr to printable time string */
526     time_t probeTime = xstat_cm_Results.probeTime;
527
528     numInt32s = xstat_cm_Results.data.AFSCB_CollData_len;
529     if (numInt32s != fullPerfInt32s) {
530         printf("** Data size mismatch in performance collection!");
531         printf("** Expecting %u, got %u\n", fullPerfInt32s, numInt32s);
532         printf("** Version mismatch with Cache Manager\n");
533         return;
534     }
535
536     printableTime = ctime(&probeTime);
537     printableTime[strlen(printableTime) - 1] = '\0';
538     fullP = (struct afs_stats_CMFullPerf *)
539         (xstat_cm_Results.data.AFSCB_CollData_val);
540
541     printf
542         ("AFSCB_XSTATSCOLL_FULL_PERF_INFO (coll %d) for CM %s\n[Probe %u, %s]\n\n",
543          xstat_cm_Results.collectionNumber, xstat_cm_Results.connP->hostName,
544          xstat_cm_Results.probeNum, printableTime);
545
546     /*
547      * Print the overall numbers first, followed by all of the RPC numbers,
548      * then each of the other groupings.
549      */
550     printf("Overall Performance Info:\n-------------------------\n");
551     PrintOverallPerfInfo(&(fullP->perf));
552     printf("\n");
553     PrintRPCPerfInfo(&(fullP->rpc));
554
555     authentP = &(fullP->authent);
556     printf("\nAuthentication info:\n--------------------\n");
557     printf
558         ("\t%u PAGS, %u records (%u auth, %u unauth), %u max in PAG, chain max: %u\n",
559          authentP->curr_PAGs, authentP->curr_Records,
560          authentP->curr_AuthRecords, authentP->curr_UnauthRecords,
561          authentP->curr_MaxRecordsInPAG, authentP->curr_LongestChain);
562     printf("\t%u PAG creations, %u tkt updates\n", authentP->PAGCreations,
563            authentP->TicketUpdates);
564     printf("\t[HWMs: %u PAGS, %u records, %u max in PAG, chain max: %u]\n",
565            authentP->HWM_PAGs, authentP->HWM_Records,
566            authentP->HWM_MaxRecordsInPAG, authentP->HWM_LongestChain);
567
568     accessinfP = &(fullP->accessinf);
569     printf("\n[Un]replicated accesses:\n------------------------\n");
570     printf
571         ("\t%u unrep, %u rep, %u reps accessed, %u max reps/ref, %u first OK\n\n",
572          accessinfP->unreplicatedRefs, accessinfP->replicatedRefs,
573          accessinfP->numReplicasAccessed, accessinfP->maxReplicasPerRef,
574          accessinfP->refFirstReplicaOK);
575
576     /* There really isn't any authorship info
577      * authorP = &(fullP->author); */
578
579 }                               /*PrintFullPerfInfo */
580
581
582 /*------------------------------------------------------------------------
583  * CM_Handler
584  *
585  * Description:
586  *      Handler routine passed to the xstat_cm module.  This handler is
587  *      called immediately after a poll of one of the Cache Managers has
588  *      taken place.  All it needs to know is exported by the xstat_cm
589  *      module, namely the data structure where the probe results are
590  *      stored.
591  *
592  * Arguments:
593  *      None.
594  *
595  * Returns:
596  *      0 on success,
597  *      -1 otherwise.
598  *
599  * Environment:
600  *      See above.  All we do now is print out what we got.
601  *
602  * Side Effects:
603  *      As advertised.
604  *------------------------------------------------------------------------*/
605
606 int
607 CM_Handler(void)
608 {                               /*CM_Handler */
609
610     static char rn[] = "CM_Handler";    /*Routine name */
611
612     printf("\n-----------------------------------------------------------\n");
613
614     /*
615      * If the probe failed, there isn't much we can do except gripe.
616      */
617     if (xstat_cm_Results.probeOK) {
618         printf("%s: Probe %u, collection %d to CM on '%s' failed, code=%d\n",
619                rn, xstat_cm_Results.probeNum,
620                xstat_cm_Results.collectionNumber,
621                xstat_cm_Results.connP->hostName, xstat_cm_Results.probeOK);
622         return (0);
623     }
624
625     if (debugging_on) {
626         int i;
627         int numInt32s = xstat_cm_Results.data.AFSCB_CollData_len;
628         afs_int32 *entry = xstat_cm_Results.data.AFSCB_CollData_val;
629
630         printf("debug: got collection number %d\n", xstat_cm_Results.collectionNumber);
631         printf("debug: collection data length is %d\n", numInt32s);
632         for (i = 0; i < numInt32s; i++) {
633             printf("debug: entry %d %u\n", i, entry[i]);
634         }
635         printf("\n");
636     }
637
638     switch (xstat_cm_Results.collectionNumber) {
639     case AFSCB_XSTATSCOLL_CALL_INFO:
640         print_cmCallStats();
641         break;
642
643     case AFSCB_XSTATSCOLL_PERF_INFO:
644         /* we will do nothing here */
645         /* PrintPerfInfo(); */
646         break;
647
648     case AFSCB_XSTATSCOLL_FULL_PERF_INFO:
649         PrintFullPerfInfo();
650         break;
651
652     default:
653         printf("** Unknown collection: %d\n",
654                xstat_cm_Results.collectionNumber);
655     }
656
657     /*
658      * Return the happy news.
659      */
660     return (0);
661
662 }                               /*CM_Handler */
663
664
665 /*------------------------------------------------------------------------
666  * CountListItems
667  *
668  * Description:
669  *      Given a pointer to the list of Cache Managers we'll be polling
670  *      (or, in fact, any list at all), compute the length of the list.
671  *
672  * Arguments:
673  *      struct cmd_item *a_firstItem : Ptr to first item in list.
674  *
675  * Returns:
676  *      Length of the above list.
677  *
678  * Environment:
679  *      Nothing interesting.
680  *
681  * Side Effects:
682  *      As advertised.
683  *------------------------------------------------------------------------*/
684
685 static int
686 CountListItems(struct cmd_item *a_firstItem)
687 {                               /*CountListItems */
688
689     int list_len;               /*List length */
690     struct cmd_item *curr_item; /*Ptr to current item */
691
692     list_len = 0;
693     curr_item = a_firstItem;
694
695     /*
696      * Count 'em up.
697      */
698     while (curr_item) {
699         list_len++;
700         curr_item = curr_item->next;
701     }
702
703     /*
704      * Return our tally.
705      */
706     return (list_len);
707
708 }                               /*CountListItems */
709
710
711 /*------------------------------------------------------------------------
712  * RunTheTest
713  *
714  * Description:
715  *      Routine called by the command line interpreter to execute the
716  *      meat of the program.  We count the number of Cache Managers
717  *      to watch, allocate enough space to remember all the connection
718  *      info for them, then go for it.
719  *
720  *
721  * Arguments:
722  *      a_s : Ptr to the command line syntax descriptor.
723  *
724  * Returns:
725  *      0, but may exit the whole program on an error!
726  *
727  * Environment:
728  *      Nothing interesting.
729  *
730  * Side Effects:
731  *      As advertised.
732  *------------------------------------------------------------------------*/
733
734 int
735 RunTheTest(struct cmd_syndesc *a_s, void *arock)
736 {                               /*RunTheTest */
737
738     static char rn[] = "RunTheTest";    /*Routine name */
739     int code;                   /*Return code */
740     int numCMs;                 /*# Cache Managers to monitor */
741     int numCollIDs;             /*# collections to fetch */
742     int currCM;                 /*Loop index */
743     int currCollIDIdx;          /*Index of current collection ID */
744     afs_int32 *collIDP;         /*Ptr to array of collection IDs */
745     afs_int32 *currCollIDP;     /*Ptr to current collection ID */
746     struct cmd_item *curr_item; /*Current CM cmd line record */
747     struct sockaddr_in *CMSktArray;     /*Cache Manager socket array */
748     struct hostent *he;         /*Host entry */
749     struct timeval tv;          /*Time structure */
750     int sleep_secs;             /*Number of seconds to sleep */
751     int initFlags;              /*Flags passed to the init fcn */
752     int waitCode;               /*Result of LWP_WaitProcess() */
753     int freq;                   /*Frequency of polls */
754     int period;                 /*Time in minutes of data collection */
755
756     /*
757      * Are we doing one-shot measurements?
758      */
759     if (a_s->parms[P_ONESHOT].items != 0)
760         one_shot = 1;
761
762     /*
763      * Are we doing debugging output?
764      */
765     if (a_s->parms[P_DEBUG].items != 0)
766         debugging_on = 1;
767
768     /*
769      * Pull out the number of Cache Managers to watch and the number of
770      * collections to get.
771      */
772     numCMs = CountListItems(a_s->parms[P_CM_NAMES].items);
773     numCollIDs = CountListItems(a_s->parms[P_COLL_IDS].items);
774
775     /* Get the polling frequency */
776     if (a_s->parms[P_FREQUENCY].items != 0)
777         freq = atoi(a_s->parms[P_FREQUENCY].items->data);
778     else
779         freq = 30;              /* default to 30 seconds */
780
781     /* Get the time duration to run the tests */
782     if (a_s->parms[P_PERIOD].items != 0)
783         period = atoi(a_s->parms[P_PERIOD].items->data);
784     else
785         period = 10;            /* default to 10 minutes */
786
787     /*
788      * Allocate the socket array.
789      */
790     if (debugging_on)
791         printf("%s: Allocating socket array for %d Cache Manager(s)\n", rn,
792                numCMs);
793     if (numCMs > 0) {
794         CMSktArray = calloc(numCMs, sizeof(struct sockaddr_in));
795         if (CMSktArray == NULL) {
796             printf("%s: Can't allocate socket array for %d Cache Managers\n",
797                    rn, numCMs);
798             exit(1);
799         }
800     } else {
801         CMSktArray = NULL;
802     }
803
804     /*
805      * Fill in the socket array for each of the Cache Managers listed.
806      */
807     curr_item = a_s->parms[P_CM_NAMES].items;
808     for (currCM = 0; currCM < numCMs; currCM++) {
809         CMSktArray[currCM].sin_family = AF_INET;
810         CMSktArray[currCM].sin_port = htons(7001);      /* Cache Manager port */
811         he = hostutil_GetHostByName(curr_item->data);
812         if (he == NULL) {
813             fprintf(stderr, "[%s] Can't get host info for '%s'\n", rn,
814                     curr_item->data);
815             exit(-1);
816         }
817         memcpy(&(CMSktArray[currCM].sin_addr.s_addr), he->h_addr, 4);
818
819         /*
820          * Move to the next CM name.
821          */
822         curr_item = curr_item->next;
823
824     }                           /*Get socket info for each Cache Manager */
825
826     /*
827      * Create and fill up the array of desired collection IDs.
828      */
829     if (debugging_on)
830         printf("Allocating %d long(s) for coll ID\n", numCollIDs);
831
832     if (numCollIDs > 0)
833         collIDP = calloc(numCollIDs, sizeof(afs_int32));
834     else
835         collIDP = NULL;
836
837     currCollIDP = collIDP;
838     curr_item = a_s->parms[P_COLL_IDS].items;
839     for (currCollIDIdx = 0; currCollIDIdx < numCollIDs; currCollIDIdx++) {
840         *currCollIDP = (afs_int32) (atoi(curr_item->data));
841         if (debugging_on)
842             printf("CollID at index %d is %d\n", currCollIDIdx, *currCollIDP);
843         curr_item = curr_item->next;
844         currCollIDP++;
845     };
846
847     /*
848      * Crank up the Cache Manager prober, then sit back and have fun.
849      */
850     printf("\nStarting up the xstat_cm service, ");
851     initFlags = 0;
852     if (debugging_on) {
853         initFlags |= XSTAT_CM_INITFLAG_DEBUGGING;
854         printf("debugging enabled, ");
855     } else
856         printf("no debugging, ");
857     if (one_shot) {
858         initFlags |= XSTAT_CM_INITFLAG_ONE_SHOT;
859         printf("one-shot operation\n");
860     } else
861         printf("continuous operation\n");
862
863     code = xstat_cm_Init(numCMs,        /*Num CMs */
864                          CMSktArray,    /*File Server socket array */
865                          freq,  /*Probe every 30 seconds */
866                          CM_Handler,    /*Handler routine */
867                          initFlags,     /*Initialization flags */
868                          numCollIDs,    /*Number of collection IDs */
869                          collIDP);      /*Ptr to collection ID array */
870     if (code) {
871         fprintf(stderr, "[%s] Error returned by xstat_cm_Init: %d\n", rn,
872                 code);
873         xstat_cm_Cleanup(1);    /*Get rid of malloc'ed structures */
874         exit(-1);
875     }
876
877     if (one_shot) {
878         /*
879          * One-shot operation; just wait for the collection to be done.
880          */
881         if (debugging_on)
882             printf("[%s] Calling LWP_WaitProcess() on event %" AFS_PTR_FMT
883                    "\n", rn, &terminationEvent);
884         waitCode = LWP_WaitProcess(&terminationEvent);
885         if (debugging_on)
886             printf("[%s] Returned from LWP_WaitProcess()\n", rn);
887         if (waitCode) {
888             if (debugging_on)
889                 fprintf(stderr,
890                         "[%s] Error %d encountered by LWP_WaitProcess()\n",
891                         rn, waitCode);
892         }
893     } else {
894         /*
895          * Continuous operation.
896          */
897         sleep_secs = 60 * period;       /*length of data collection */
898         printf
899             ("xstat_cm service started, main thread sleeping for %d secs.\n",
900              sleep_secs);
901
902         /*
903          * Let's just fall asleep for a while, then we'll clean up.
904          */
905         tv.tv_sec = sleep_secs;
906         tv.tv_usec = 0;
907         code = IOMGR_Select(0,  /*Num fds */
908                             0,  /*Descriptors ready for reading */
909                             0,  /*Descriptors ready for writing */
910                             0,  /*Descriptors with exceptional conditions */
911                             &tv);       /*Timeout structure */
912         if (code) {
913             fprintf(stderr,
914                     "[%s] IOMGR_Select() returned non-zero value: %d\n", rn,
915                     code);
916         }
917     }
918
919     /*
920      * We're all done.  Clean up, put the last nail in Rx, then
921      * exit happily.
922      */
923     if (debugging_on)
924         printf("\nYawn, main thread just woke up.  Cleaning things out...\n");
925     code = xstat_cm_Cleanup(1); /*Get rid of malloc'ed data */
926     rx_Finalize();
927     return (0);
928
929 }                               /*RunTheTest */
930
931
932 #include "AFS_component_version_number.c"
933 int
934 main(int argc, char **argv)
935 {                               /*Main routine */
936
937     static char rn[] = "xstat_cm_test"; /*Routine name */
938     afs_int32 code;     /*Return code */
939     struct cmd_syndesc *ts;     /*Ptr to cmd line syntax desc */
940
941     /*
942      * Set up the commands we understand.
943      */
944     ts = cmd_CreateSyntax("initcmd", RunTheTest, NULL, "initialize the program");
945     cmd_AddParm(ts, "-cmname", CMD_LIST, CMD_REQUIRED,
946                 "Cache Manager name(s) to monitor");
947     cmd_AddParm(ts, "-collID", CMD_LIST, CMD_REQUIRED,
948                 "Collection(s) to fetch");
949     cmd_AddParm(ts, "-onceonly", CMD_FLAG, CMD_OPTIONAL,
950                 "Collect results exactly once, then quit");
951     cmd_AddParm(ts, "-frequency", CMD_SINGLE, CMD_OPTIONAL,
952                 "poll frequency, in seconds");
953     cmd_AddParm(ts, "-period", CMD_SINGLE, CMD_OPTIONAL,
954                 "data collection time, in minutes");
955     cmd_AddParm(ts, "-debug", CMD_FLAG, CMD_OPTIONAL,
956                 "turn on debugging output");
957
958     /*
959      * Parse command-line switches & execute the test, then get the
960      * heck out of here.
961      */
962     code = cmd_Dispatch(argc, argv);
963     if (code) {
964         fprintf(stderr, "[%s] Call to cmd_Dispatch() failed; code is %d\n",
965                 rn, code);
966     }
967
968     exit(code);
969
970 }                               /*Main routine */