quick implementation to make projections to have object id for array elements. last...
[charm.git] / src / ck-perf / trace-counter.C
1 /*****************************************************************************
2  * $Source$
3  * $Author$
4  * $Date$
5  * $Revision$
6  *****************************************************************************/
7
8 /**
9  * \addtogroup CkPerf
10 */
11 /*@{*/
12
13 // http://www.scl.ameslab.gov/Projects/Rabbit/
14 // http://www.support.compaq.com/nttools
15
16 #include "charm++.h"
17 #ifdef CMK_ORIGIN2000
18 #include "trace-counter.h"
19 #include "ck.h"
20 #include <inttypes.h>
21 #include <values.h>
22
23 #define DEBUGF(x) // CmiPrintf x
24 #define VER 1.0
25
26 // for performance monitoring
27 extern "C" int start_counters(int e0, int e1);
28 extern "C" int read_counters(int e0, long long *c0, int e1, long long *c1);
29
30 // a message will be composed of 2*numCounters + NUM_EXTRA_PERF
31 // reduction[0-(numStats-1)] correspond to counters 0-(numStats-1)
32 // the next is phase (only write if CmiMyPe() == 0)
33 // the next is the idle time (if any calculated)
34 // the next four are (respectively) flops/s, l1 miss%, l2 miss%, tlb miss%
35 // write -1 if not able to calculate
36 static const int NUM_EXTRA_PERF = 6;
37
38 CkpvStaticDeclare(Trace*, _trace);
39 CpvStaticDeclare(CountLogPool*, _logPool);
40 CpvStaticDeclare(char*,  _logName);
41 CpvStaticDeclare(char**, _counterNames);
42 CpvStaticDeclare(char**, _counterDesc);
43 CpvStaticDeclare(int,    _numCounters);
44 CpvStaticDeclare(int,    _reductionID);
45 static int _numEvents = 0;
46 CpvDeclare(double, version);
47
48 // a rudimentary reduction to print out the performance results across the run
49 CmiHandler StatTableReduction(char* msg)
50 {
51   DEBUGF(("StatTableReduction called\n", CmiMyPe(), CmiNumPes()));
52   static double* reduce = NULL;
53   static int numReduce = 0;
54   int numCounters = CpvAccess(_numCounters);
55   int size = 2*CpvAccess(_numCounters)+NUM_EXTRA_PERF;
56   int i;
57   if (reduce == NULL) {
58     // allocate
59     reduce = new double[size];
60     for (i=0; i<size; i++) { reduce[i] = 0.0; }
61     DEBUGF(("  allocated reduce numCounters %d size %d\n", 
62             numCounters, size));
63   }
64
65   // see above for the feilds of this message
66   double* msgResults = (double *)(msg+ALIGN8(CmiMsgHeaderSizeBytes));
67   for (i=0; i<size; i++) { 
68     reduce[i] += msgResults[i]; 
69   }
70   
71   char** counterNames = CpvAccess(_counterNames);
72   numReduce++;
73   DEBUGF(("      numReduce %d numPes %d\n", numReduce, CmiNumPes()));
74   int phase = reduce[2*numCounters];
75   if (numReduce >= CmiNumPes()) {
76     // finished with reduction, print out results
77     numReduce = 0;
78     for (i=0; i<numCounters; i++) {
79       if (reduce[2*i+1]>0.0) { // is time > 0?
80         if (phase >= 0) {
81           CmiPrintf("PHASE %d %s totalCount %f totalTime (us) %f\n" 
82                     "PHASE %d %s count/proc %f avgTime (us)/phase %f\n",
83                     phase, counterNames[i], reduce[2*i], reduce[2*i+1]*1e6,
84                     phase, counterNames[i], reduce[2*i]/CmiNumPes(), 
85                     reduce[2*i+1]*1e6/CmiNumPes());
86         }
87         else {
88           CmiPrintf("%s totalCount %f totalTime (us) %f\n" 
89                     "%s count/proc %f avgTime (us)/phase %f\n",
90                     counterNames[i], reduce[2*i], reduce[2*i+1]*1e6,
91                     counterNames[i], reduce[2*i]/CmiNumPes(), 
92                     reduce[2*i+1]*1e6/CmiNumPes());
93         }
94       }
95     }
96     if (phase >= 0) {
97       CmiPrintf("PHASE %d totalIdleTime (us) %f avgIdleTime (us)/phase %f\n",
98                 phase, reduce[2*numCounters+1]*1e6, 
99                 reduce[2*numCounters+1]*1e6/CmiNumPes());
100     }
101     else {
102       CmiPrintf("totalIdleTime (us) %f avgIdleTime (us)/phase %f\n",
103                 reduce[2*numCounters+1]*1e6, 
104                 reduce[2*numCounters+1]*1e6/CmiNumPes());
105     }
106     if (reduce[2*numCounters+2] > 0.0) {
107       // we have flops
108       if (phase >= 0) {
109         CmiPrintf("PHASE %d flops/s %f flops/s/PE %f\n",
110                   phase, reduce[2*numCounters+2], 
111                   reduce[2*numCounters+2]/CmiNumPes());
112       }
113       else {
114         CmiPrintf("flops/s %f flops/s/PE %f\n",
115                   reduce[2*numCounters+2], 
116                   reduce[2*numCounters+2]/CmiNumPes());
117       }
118     }
119     char* missRate = NULL;
120     for (i=0; i<3; i++) {
121       switch (i) {
122       case 0: missRate = "l1 avg miss rate (%)";  break;
123       case 1: missRate = "l2 avg miss rate (%)";  break;
124       case 2: missRate = "tlb avg miss rate (%)";  break;
125       }
126       if (reduce[2*numCounters+3+i] >= 0.0) {
127         if (phase >= 0) {
128           CmiPrintf("PHASE %d %s %f\n", 
129                     phase, missRate, reduce[2*numCounters+3+i]/CmiNumPes()*100);
130         }
131         else {
132           CmiPrintf("%s %f\n", 
133                     missRate, reduce[2*numCounters+3+i]/CmiNumPes()*100);
134         }
135       }
136     }
137
138     // clean up
139     delete [] reduce;
140     reduce = NULL;
141   }
142   CmiFree(msg);
143 }
144
145 //! The following is the list of arguments that can be passed to 
146 //!   the +counter{1|2} command line arguments.
147 //! To add or change, change NUM_COUNTER_ARGS and follow the examples
148 //! Use three constructor arguments:
149 //!   1) Code (for SGI libperfex) associated with counter.
150 //!   2) String to be entered on the command line.
151 //!   3) String that is the description of the counter.
152 //! All NUM_COUNTER_ARGS are automatically registered via
153 //!   TraceCounter::TraceCounter() definition.
154 static const int NUM_COUNTER_ARGS = 32;
155 static TraceCounter::CounterArg COUNTER_ARG[NUM_COUNTER_ARGS] = 
156 { TraceCounter::CounterArg( 0, "CYCLES0",      "Cycles (also see code 16)"),
157   TraceCounter::CounterArg( 1, "INSTR",        "Issued instructions"),
158   TraceCounter::CounterArg( 2, "LOAD",         "Issued loads"),
159   TraceCounter::CounterArg( 3, "STORE",        "Issued stores"),
160   TraceCounter::CounterArg( 4, "STORE_COND",   "Issued store conditionals"),
161   TraceCounter::CounterArg( 5, "FAIL_COND",    "Failed store conditionals"),
162   TraceCounter::CounterArg( 6, "DECODE_BR",    "Decoded branches.  (This changes meaning in 3.x versions of R10000.  It becomes resolved branches)"),
163   TraceCounter::CounterArg( 7, "QUADWORDS2",   "Quadwords written back from secondary cache"),
164   TraceCounter::CounterArg( 8, "CACHE_ER2",    "Correctable secondary cache data array ECC errors"),
165   TraceCounter::CounterArg( 9, "L1_IMISS",     "Primary (L1) instruction cache misses"),
166   TraceCounter::CounterArg(10, "L2_IMISS",     "Secondary (L2) instruction cache misses"),
167   TraceCounter::CounterArg(11, "INSTRMISPR",   "Instruction misprediction from secondary cache way prediction table"),
168   TraceCounter::CounterArg(12, "EXT_INTERV",   "External interventions"),
169   TraceCounter::CounterArg(13, "EXT_INVAL",    "External invalidations"),
170   TraceCounter::CounterArg(14, "VIRT_COHER",   "Virtual coherency conditions.  (This changes meaning in 3.x versions of R10000.  It becomes ALU/FPU forward progress cycles.  On the R12000, this counter is always 0)."),
171   TraceCounter::CounterArg(15, "GR_INSTR15",   "Graduated instructions (also see code 17)"),
172   TraceCounter::CounterArg(16, "CYCLES16",     "Cycles (also see code 0)"),
173   TraceCounter::CounterArg(17, "GR_INSTR17",   "Graduated instructions (also see code 15)"),
174   TraceCounter::CounterArg(18, "GR_LOAD" ,     "Graduated loads"),
175   TraceCounter::CounterArg(19, "GR_STORE",     "Graduated stores"),
176   TraceCounter::CounterArg(20, "GR_ST_COND",   "Graduated store conditionals"),
177   TraceCounter::CounterArg(21, "GR_FLOPS",     "Graduated floating point instructions"),
178   TraceCounter::CounterArg(22, "QUADWORDS1",   "Quadwords written back from primary data cache"),
179   TraceCounter::CounterArg(23, "TLB_MISS",     "TLB misses"),
180   TraceCounter::CounterArg(24, "MIS_BR",       "Mispredicted branches"),
181   TraceCounter::CounterArg(25, "L1_DMISS",     "Primary (L1) data cache misses"),
182   TraceCounter::CounterArg(26, "L2_DMISS",     "Primary (L2) data cache misses"),
183   TraceCounter::CounterArg(27, "DATA_MIS",     "Data misprediction from secondary cache way predicition table"),
184   TraceCounter::CounterArg(28, "EXT_INTERV2",  "External intervention hits in secondary cache (L2)"),
185   TraceCounter::CounterArg(29, "EXT_INVAL2",   "External invalidation hits in secondary cache"),
186   TraceCounter::CounterArg(30, "CLEAN_ST_PRE", "Store/prefetch exclusive to clean block in secondary cache"),
187   TraceCounter::CounterArg(31, "SHARE_ST_PRE", "Store/prefetch exclusive to shared block in secondary cache") 
188 };
189
190 // this is called by the Charm++ runtime system
191 void _createTracecounter(char **argv)
192 {
193   DEBUGF(("%d/%d DEBUG: createTraceCounter\n", CmiMyPe(), CmiNumPes()));
194   CkpvInitialize(Trace*, _trace);
195   TraceCounter* tc = new TraceCounter();  _MEMCHECK(tc);
196   tc->traceInit(argv);
197   CkpvAccess(_trace) = tc;
198   CkpvAccess(_traces)->addTrace(CkpvAccess(_trace));
199 }
200
201 extern "C"
202 void traceCounterBeginIdle(void)
203 {
204   CkpvAccess(_trace)->beginIdle();
205 }
206
207 extern "C"
208 void traceCounterEndIdle(void)
209 {
210   CkpvAccess(_trace)->endIdle();
211 }
212
213 // constructor
214 StatTable::StatTable():
215   stats_(NULL), numStats_(0)
216 {
217   DEBUGF(("%d/%d DEBUG: StatTable::StatTable %08x size %d\n", 
218           CmiMyPe(), CmiNumPes(), this));
219
220   DEBUGF(("%d/%d registering reductionID\n", CmiMyPe(), CmiNumPes()));
221   CpvAccess(_reductionID) = CmiRegisterHandler((CmiHandler)StatTableReduction); 
222 }
223
224 // destructor
225 StatTable::~StatTable() { if (stats_ != NULL) { delete [] stats_; } }
226
227 // initialize the stat table internals
228 void StatTable::init(int argc)
229 {
230   char** counterNames = CpvAccess(_counterNames);
231   char** counterDesc = CpvAccess(_counterDesc);
232
233   if (argc > numStats_) {
234     if (stats_ != NULL) { delete [] stats_; }
235     stats_ = new Statistics[argc];  _MEMCHECK(stats_);
236     numStats_ = argc;
237   }
238   for (int i=0; i<argc; i++) { 
239     DEBUGF(("%d/%d DEBUG:   %d name %s\n     desc %s\n", 
240             CmiMyPe(), CmiNumPes(), i, name[i], desc[i]));
241     stats_[i].name = counterNames[i]; 
242     stats_[i].desc = counterDesc[i];
243   }
244   clear();
245 }
246
247 //! one entry is called for 'time' seconds, value is counter reading
248 void StatTable::setEp(int epidx, int stat, long long value, double time) 
249 {
250   // CmiPrintf("StatTable::setEp %08x %d %d %d %f\n", 
251   //           this, epidx, stat, value, time);
252
253   CkAssert(epidx<MAX_ENTRIES);
254   CkAssert(stat<numStats_ && stat>=0);
255   
256   int numCalled = stats_[stat].numCalled[epidx];
257   double avg = stats_[stat].avgCount[epidx];
258   double stdDev = stats_[stat].stdDevCount[epidx];
259   stats_[stat].numCalled[epidx]++;
260   stats_[stat].avgCount[epidx] = (avg * numCalled + value) / (numCalled + 1);
261   // note that the stddev calculation is not quite correct, but it's
262   // almost correct and over time will converge with true value
263   avg = stats_[stat].avgCount[epidx];
264   stats_[stat].stdDevCount[epidx] = 
265     (stdDev * numCalled + (value-avg)*(value-avg)) / (numCalled+1);
266   // CmiPrintf("variance %f avg %f value %ld calcVariance %f\n", 
267   // stdDev, avg, value, stats_[stat].stdDevCount[epidx]);
268   stats_[stat].totTime[epidx] += time;
269   if (stats_[stat].maxCount[epidx] < value) {
270     stats_[stat].maxCount[epidx] = value;
271   }
272   if (stats_[stat].minCount[epidx] > value) {
273     stats_[stat].minCount[epidx] = value;
274   }
275 }
276
277 //! write three lines for each stat:
278 //!   1. number of calls for each entry
279 //!   2. average count for each entry
280 //!   3. total time in us spent for each entry
281 void StatTable::write(FILE* fp) 
282 {
283   DEBUGF(("%d/%d DEBUG: Writing StatTable\n", CmiMyPe(), CmiNumPes()));
284   int i, j;
285   int _numEntries=_entryTable.size();
286   for (i=0; i<numStats_; i++) {
287     // FAKE OUT AND WRITE AN OVERVIEW AS LAST ENTRY
288     // write description of the entry
289     fprintf(fp, "[%s {%s}]\n", stats_[i].name, stats_[i].desc);
290     // write number of calls for each entry
291     fprintf(fp, "[%s num_called] ", stats_[i].name);
292     for (j=0; j<_numEntries+1; j++) { 
293       fprintf(fp, "%d ", stats_[i].numCalled[j]); 
294     }
295     fprintf(fp, "\n");
296     // write average count for each 
297     fprintf(fp, "[%s avg_count] ", stats_[i].name);
298     for (j=0; j<_numEntries+1; j++) { 
299       fprintf(fp, "%f ", stats_[i].avgCount[j]); 
300     }
301     fprintf(fp, "\n");
302     // write standard deviation of count for each 
303     fprintf(fp, "[%s std_dev_count] ", stats_[i].name);
304     for (j=0; j<_numEntries+1; j++) { 
305       fprintf(fp, "%f ", sqrt(stats_[i].stdDevCount[j])); 
306     }
307     fprintf(fp, "\n");
308     // write total time in us spent for each entry
309     fprintf(fp, "[%s total_time(us)] ", stats_[i].name);
310     for (j=0; j<_numEntries+1; j++) {
311       fprintf(fp, "%f ", stats_[i].totTime[j]*1e6);
312     }
313     fprintf(fp, "\n");
314     // write max count for each entry
315     fprintf(fp, "[%s max_count] ", stats_[i].name);
316     for (j=0; j<_numEntries+1; j++) { 
317       fprintf(fp, "%ld ", stats_[i].maxCount[j]); 
318     }
319     fprintf(fp, "\n");
320     // write min count for each entry
321     fprintf(fp, "[%s min_count] ", stats_[i].name);
322     for (j=0; j<_numEntries+1; j++) { 
323       if (stats_[i].minCount[j] == MAXLONGLONG) { stats_[i].minCount[j] = 0; }
324       fprintf(fp, "%ld ", stats_[i].minCount[j]); 
325     }
326     fprintf(fp, "\n");
327   }
328   DEBUGF(("%d/%d DEBUG: Finished writing StatTable\n", CmiMyPe(), CmiNumPes()));
329 }
330
331 //! set all of internals to null
332 void StatTable::clear() 
333 {
334   for (int i=0; i<numStats_; i++) {
335     for (int j=0; j<MAX_ENTRIES; j++) {
336       stats_[i].numCalled[j] = 0;
337       stats_[i].avgCount[j] = 0.0;
338       stats_[i].stdDevCount[j] = 0.0;
339       stats_[i].totTime[j] = 0.0;
340       stats_[i].maxCount[j] = 0.0;
341       stats_[i].minCount[j] = MAXLONGLONG;
342     }
343   }
344 }
345
346 //! do a reduction across processors to calculate the total count for
347 //! each count, and if the count has flops, etc, then calc the 
348 //! the flops/s, etc...
349 void StatTable::doReduction(int phase, double idleTime) {
350   DEBUGF(("%d/%d DEBUG: StatTable::doReduction()\n",
351           CmiMyPe(), CmiNumPes(), this));
352   // see above (NUM_EXTRA_PERF) for the fields in the message
353   int msgSize = 
354     ALIGN8(CmiMsgHeaderSizeBytes)+
355     sizeof(double)*(2*numStats_+NUM_EXTRA_PERF);
356   char *msg = (char *)CmiAlloc(msgSize);
357   double* reduction = (double*)(msg+ALIGN8(CmiMsgHeaderSizeBytes));
358   // calculate flops/s, l1%, l2%, tlb% if it's there
359   char** counterNames = CpvAccess(_counterNames);
360   int GR_FLOPS = -1;  double flopsRate = -1.0;  
361   int LOAD     = -1;  double loadRate = -1.0;
362   int STORE    = -1;  double storeRate = -1.0;
363   int L1_DMISS = -1;  double l1Rate = -1.0;
364   int L2_DMISS = -1;  double l2Rate = -1.0;
365   int TLB_MISS = -1;  double tlbRate = -1.0;
366   int i, j;
367   for (i=0; i<2*numStats_+NUM_EXTRA_PERF; i++) { reduction[i] = 0.0; }
368   for (i=0; i<numStats_; i++) {
369     for (int j=0; j<MAX_ENTRIES; j++) { 
370       reduction[2*i] += stats_[i].numCalled[j]*stats_[i].avgCount[j]; 
371       reduction[2*i+1] += stats_[i].totTime[j];
372     }
373     if (strcmp(counterNames[i], "GR_FLOPS")==0) { GR_FLOPS = i; }
374     else if (strcmp(counterNames[i], "LOAD")==0) { LOAD = i; }
375     else if (strcmp(counterNames[i], "STORE")==0) { STORE = i; }
376     else if (strcmp(counterNames[i], "L1_DMISS")==0) { L1_DMISS = i; }
377     else if (strcmp(counterNames[i], "L2_DMISS")==0) { L2_DMISS = i; }
378     else if (strcmp(counterNames[i], "TLB_MISS")==0) { TLB_MISS = i; }
379   }
380   if (CmiMyPe()==0) { reduction[2*numStats_] = phase; }
381   reduction[2*numStats_+1] = idleTime;  
382   // -1 for the rest of the calc values
383   reduction[2*numStats_+2] = -1.0;
384   reduction[2*numStats_+3] = -1.0;
385   reduction[2*numStats_+4] = -1.0;
386   reduction[2*numStats_+5] = -1.0;
387   // calculate flops/s, l1%, l2%, tlb% if it's there
388   double* rate = NULL;
389   int  index;
390   for (i=0; i<6; i++) {
391     switch (i) {
392     case 0: rate = &flopsRate; index = GR_FLOPS;   break;
393     case 1: rate = &loadRate;  index = LOAD;       break;
394     case 2: rate = &storeRate; index = STORE;      break;
395     case 3: rate = &l1Rate;    index = L1_DMISS;   break;
396     case 4: rate = &l2Rate;    index = L2_DMISS;   break; 
397     case 5: rate = &tlbRate;   index = TLB_MISS;   break;
398     }
399     if (index >= 0 && reduction[2*index+1] > 0.0) { 
400       // if we have the counter AND it's times were non-zero
401       *rate = reduction[2*index]/reduction[2*index+1]; 
402     }
403   }
404   // store rates if there
405   if (GR_FLOPS >= 0) { reduction[2*numStats_+2] = flopsRate; }
406   if (LOAD >= 0 && STORE >= 0) {
407     double memRate = loadRate + storeRate;
408     if (L1_DMISS >= 0 & memRate > 0) { 
409       reduction[2*numStats_+3] = l1Rate / memRate; 
410     }
411     if (L2_DMISS >= 0 & memRate > 0) { 
412       reduction[2*numStats_+4] = l2Rate / memRate; 
413     }
414     if (TLB_MISS >= 0 & memRate > 0) { 
415       reduction[2*numStats_+5] = tlbRate / memRate; 
416     }
417   }
418
419   // send the data
420   CmiSetHandler(msg, (int)CpvAccess(_reductionID));
421   int handlerID = CmiGetHandler(msg);
422   DEBUGF(("%d/%d handlerID %d reductionID %d\n", 
423           CmiMyPe(), CmiNumPes(), handlerID, CpvAccess(_reductionID)));
424   CmiSyncSendAndFree(0, msgSize, msg);
425 }
426
427 CountLogPool::CountLogPool():
428   stats_     (),
429   lastPhase_ (-1)
430 {
431   DEBUGF(("%d/%d DEBUG: CountLogPool::CountLogPool() %08x\n", 
432           CmiMyPe(), CmiNumPes(), this));
433 }
434
435 // open file, if phase is -1, don't add the phase string
436 FILE* CountLogPool::openFile(int phase) {
437   // technically, the sprintf into pestr & phasestr are unnecessary,
438   // can just make a limit and check for that
439
440   DEBUGF(("%d CountLogPool::openFile(%d)\n", CmiMyPe(), phase));
441   const static int strSize = 10;
442   char pestr[strSize+1];
443   char phasestr[strSize+1];
444   snprintf(pestr, strSize, "%d", CmiMyPe());
445   pestr[strSize] = '\0';
446   int len = strlen(CpvAccess(_logName)) + strlen("phase.count.") + 2*strSize + 1;
447   char* fname = new char[len+1];  _MEMCHECK(fname);
448   if (phase >= 0) { 
449     snprintf(phasestr, strSize, "%d", phase);
450     phasestr[strSize] = '\0';
451     sprintf(fname, "%s.phase%s.%s.count", CpvAccess(_logName), phasestr, pestr); 
452   }
453   else { sprintf(fname, "%s.%s.count", CpvAccess(_logName), pestr); }
454   FILE* fp = NULL;
455   DEBUGF(("%d/%d DEBUG: TRACE: %s:%d\n", CmiMyPe(), CmiNumPes(), fname, errno));
456   do {
457     fp = fopen(fname, "w+");
458   } while (!fp && errno == EINTR);
459   delete[] fname;
460   if(!fp) {
461     CmiAbort("Cannot open Summary Trace File for writing...\n");
462   }
463   return fp;
464 }
465
466 void CountLogPool::write(int phase) 
467 {
468   if (phase >= 0) { lastPhase_ = phase; }
469   if (phase < 0 && lastPhase_ >= 0) { lastPhase_++;  phase = lastPhase_; }
470
471   int _numEntries=_entryTable.size();
472   FILE* fp = (phase==-1) ? openFile() : openFile(phase); 
473   fprintf(fp, "ver:%3.1f %d/%d ep:%d counters:%d\n", 
474           CpvAccess(version), CmiMyPe(), CmiNumPes(), _numEntries+1, 
475           stats_.numStats());
476   stats_.write(fp);
477   fclose(fp);
478 }
479
480 void CountLogPool::writeSts(int phase)
481 {
482   // technically, the sprintf into phasestr is unnecessary,
483   // can just make a limit and check for that
484
485   if (phase >= 0) { lastPhase_ = phase; }
486   DEBUGF(("%d CountLogPool::writeSts(%d)\n", CmiMyPe(), phase));
487
488   const static int strSize = 10;
489   char phasestr[strSize+1];
490   int _numEntries=_entryTable.size();
491   // add strSize for phase number
492   char *fname = 
493     new char[strlen(CpvAccess(_logName))+strlen(".count.sts")+strSize];
494   _MEMCHECK(fname);
495   if (phase < 0 && lastPhase_ >= 0) { phase = lastPhase_; }
496   if (phase >= 0) { 
497     snprintf(phasestr, strSize, "%d", phase);
498     phasestr[strSize] = '\0';
499     sprintf(fname, "%s.phase%s.count.sts", CpvAccess(_logName), phasestr); 
500   } 
501   else { sprintf(fname, "%s.count.sts", CpvAccess(_logName)); }
502   FILE *sts = fopen(fname, "w+");
503   // DEBUGF(("%d/%d DEBUG: File: %s \n", CmiMyPe(), CmiNumPes(), fname));
504   if(sts==0)
505     CmiAbort("Cannot open summary sts file for writing.\n");
506   CmiPrintf("WRITING FILE=%s\n", fname); 
507   delete[] fname;
508   fprintf(sts, "MACHINE %s\n",CMK_MACHINE_NAME);
509   fprintf(sts, "PROCESSORS %d\n", CmiNumPes());
510   fprintf(sts, "TOTAL_CHARES %d\n", _chareTable.size());
511   fprintf(sts, "TOTAL_EPS %d\n", _numEntries+1);  // make extra overview
512   fprintf(sts, "TOTAL_MSGS %d\n", _msgTable.size());
513   fprintf(sts, "TOTAL_PSEUDOS %d\n", 0);
514   fprintf(sts, "TOTAL_EVENTS %d\n", _numEvents);
515   int i;
516   for(i=0;i<_chareTable.size();i++)
517     fprintf(sts, "CHARE %d %s\n", i, _chareTable[i]->name);
518   for(i=0;i<_numEntries;i++)
519     fprintf(sts, "ENTRY CHARE %d %s %d %d\n", i, _entryTable[i]->name,
520                  _entryTable[i]->chareIdx, _entryTable[i]->msgIdx);
521   // fake out, make OVERVIEW
522   fprintf(sts, "ENTRY CHARE %d OVERVIEW -1 -1\n", _numEntries);
523   for(i=0;i<_msgTable.size();i++)
524     fprintf(sts, "MESSAGE %d %ld\n", i, _msgTable[i]->size);
525   for(i=0;i<_numEvents;i++)
526     fprintf(sts, "EVENT %d Event%d\n", i, i);
527   fprintf(sts, "END\n");
528   fclose(sts);
529 }
530
531 void CountLogPool::setEp(int epidx, 
532                          int index1, long long count1, 
533                          int index2, long long count2, 
534                          double time) 
535 {
536   // DEBUGF(("%d/%d DEBUG: CountLogPool::setEp %08x %d %d %d %f\n", 
537   //        CmiMyPe(), CmiNumPes(), this, epidx, count1, count2, time));
538
539   if (epidx >= MAX_ENTRIES) {
540     CmiAbort("CountLogPool::setEp too many entry points!\n");
541   }
542   stats_.setEp(epidx, index1, count1, time);
543   stats_.setEp(epidx, index2, count2, time);
544 }
545
546 //! constructor
547 TraceCounter::TraceCounter() :
548   // comand line processing
549   firstArg_      (NULL),
550   lastArg_       (NULL),
551   argStrSize_    (0),
552   commandLine_   (NULL),
553   commandLineSz_ (0),
554   counter1_      (NULL),
555   counter2_      (NULL),
556   counter1Sz_    (0),
557   counter2Sz_    (0),
558   // result of different command line opts
559   overview_      (false),
560   switchRandom_  (false),
561   switchByPhase_ (false),
562   noLog_         (false),
563   writeByPhase_  (false),
564   // store between start/stop of counter read
565   execEP_        (-1),
566   startEP_       (0.0),
567   genStart_      (-1),
568   // store state
569   idleTime_      (0.0),
570   phase_         (-1),
571   reductionPhase_(-1),
572   traceOn_       (false),
573   status_        (IDLE),
574   dirty_         (false)
575 {
576   for (int i=0; i<NUM_COUNTER_ARGS; i++) { registerArg(&COUNTER_ARG[i]); }
577 }
578
579 //! destructor
580 TraceCounter::~TraceCounter() { 
581   if (commandLine_ != NULL) { delete [] commandLine_; }
582   traceClose(); 
583 }
584
585 //! process command line arguments!
586 void TraceCounter::traceInit(char **argv)
587 {
588   CpvInitialize(CountLogPool*, _logPool);
589   CpvInitialize(char*, _logName);
590   CpvInitialize(double, version);
591   CpvInitialize(char**, _counterNames);
592   CpvInitialize(char**, _counterDesc);
593   CpvInitialize(int,    _numCounters);
594   CpvInitialize(int, _reductionID);
595
596   CpvAccess(_logName) = (char *) malloc(strlen(argv[0])+1);
597   _MEMCHECK(CpvAccess(_logName));
598   strcpy(CpvAccess(_logName), argv[0]);
599   CpvAccess(version) = VER;
600
601   int i;
602   // parse command line args
603   char* counters = NULL;
604   CounterArg* commandLine_ = NULL;
605   bool badArg = false;
606   int numCounters = 0;
607   if (CmiGetArgStringDesc(argv, "+counters", &counters, "Measure these performance counters")) {
608     if (CmiMyPe()==0) { CmiPrintf("Counters: %s\n", counters); }
609     int offset = 0;
610     int limit = strlen(counters);
611     char* ptr = counters;
612     while (offset < limit && 
613            (ptr = strtok(&counters[offset], ",")) != NULL) 
614     { 
615       offset += strlen(ptr)+1;
616       ptr = &ptr[strlen(ptr)+1];
617       numCounters++; 
618     }
619     if (CmiMyPe()==0) { 
620       CmiPrintf("There are %d counters\n", numCounters); 
621     }
622     commandLine_ = new CounterArg[numCounters];
623     ptr = counters;
624     for (i=0; i<numCounters; i++) {
625       commandLine_[i].arg = ptr;
626       if (!matchArg(&commandLine_[i])) { 
627         if (CmiMyPe()==0) { CmiPrintf("Bad arg: [%s]\n", ptr); }
628         badArg = true; 
629       }
630       ptr = &ptr[strlen(ptr)+1];
631     }
632   }
633   commandLineSz_ = numCounters;
634
635   // check to see if args are valid, output if not
636   if (badArg || CmiGetArgFlagDesc(argv, "+count-help", "List available performance counters")) {
637     if (CmiMyPe() == 0) { printHelp(); }
638     ConverseExit();  return;
639   }
640   else if (counters == NULL) {
641     if (CmiMyPe() == 0) { usage(); }
642     ConverseExit();  return;
643   }
644
645   // get optional command line args
646   overview_      = CmiGetArgFlag(argv, "+count-overview");  
647   switchRandom_  = CmiGetArgFlag(argv, "+count-switchrandom");  
648   switchByPhase_ = CmiGetArgFlag(argv, "+count-switchbyphase");
649   noLog_         = CmiGetArgFlag(argv, "+count-nolog");
650   writeByPhase_  = CmiGetArgFlag(argv, "+count-writebyphase");
651   char* logName  = NULL;
652   if (CmiGetArgString(argv, "+count-logname", &logName)) {
653     CpvAccess(_logName) = logName;
654     if (noLog_) {
655       if (CkMyPe()==0) {
656         CmiPrintf("+count-logname and +count-nolog are MUTUALLY EXCLUSIVE\n");
657         usage();
658         CmiAbort("");
659       }
660     }
661   }
662   if (switchByPhase_ && overview_) {
663     if (CkMyPe()==0) {
664       CmiPrintf(
665         "+count-switchbyphase and +count-overview are MUTUALLY EXCLUSIVE\n"
666         "+count-overview automatically switches by phase.\n");
667       usage();
668       CmiAbort("");
669     }
670   }
671   if (writeByPhase_ && noLog_) {
672     if (CkMyPe()==0) {
673       CmiPrintf("+count-writebyphase and +count-nolog are MUTUALLY EXCLUSIVE\n");
674       usage();
675       CmiAbort("");
676     }
677   }
678
679   // parse through commandLine_, figure out which belongs on which list (1 vs 2)
680   CounterArg* last1 = NULL;
681   CounterArg* last2 = NULL;
682   CounterArg* tmp = NULL;
683   counter1Sz_ = counter2Sz_ = 0;
684   for (i=0; i<commandLineSz_; i++) {
685     tmp = &commandLine_[i];
686     if (tmp->code < NUM_COUNTER_ARGS/2) {
687       if (counter1_ == NULL) { counter1_ = tmp;  last1 = counter1_; }
688       else { last1->next = tmp;  last1 = tmp; }
689       counter1Sz_++;
690     }
691     else {
692       if (counter2_ == NULL) { counter2_ = tmp;  last2 = counter2_; }
693       else { last2->next = tmp;  last2 = tmp; }
694       counter2Sz_++;
695     }
696   }
697   if (counter1_ == NULL) {
698     printHelp();
699     if (CmiMyPe()==0) {
700       CmiPrintf("\nMust specify some counters with code < %d\n", 
701                 NUM_COUNTER_ARGS/2);
702     }
703     ConverseExit();
704   }
705   if (counter2_ == NULL) {
706     printHelp();
707     if (CmiMyPe()==0) {
708       CmiPrintf("\nMust specify some counters with code >= %d\n", 
709                 NUM_COUNTER_ARGS/2);
710     }
711     ConverseExit();
712   }
713   last1->next = counter1_;
714   last2->next = counter2_;
715
716   // all args valid, now set up logging
717   if (CmiMyPe() == 0) {
718     CmiPrintf("Running with tracemode=counter and args:\n");
719     // print out counter1 set
720     tmp = counter1_;
721     i = 0;
722     do {
723       CmiPrintf("  <counter1-%d>=%d %s %s\n", i, tmp->code, tmp->arg, tmp->desc);
724       tmp = tmp->next;
725       i++;
726     } while (tmp != counter1_);
727     // print out counter2 set
728     tmp = counter2_;
729     i = 0;
730     do {
731       CmiPrintf("  <counter2-%d>=%d %s %s\n", i, tmp->code, tmp->arg, tmp->desc);
732       tmp = tmp->next;
733       i++;
734     } while (tmp != counter2_);
735
736     CmiPrintf(
737       "+count-overview %d\n+count-switchrandom %d\n"
738       "+count-switchbyphase %d\n+count-nolog %d\n"
739       "+count-logname %s\n+count-writebyphase %d\n",
740       overview_, switchRandom_, switchByPhase_, noLog_, 
741       logName, writeByPhase_);
742   }
743
744   // DEBUGF(("    DEBUG: Counter1=%d Counter2=%d\n", counter1_, counter2_));
745   CpvAccess(_logPool) = new CountLogPool();
746
747   // allocate names so can do reduction/analysis on the fly
748   char** counterNames = new char*[counter1Sz_+counter2Sz_];
749   char** counterDesc = new char*[counter1Sz_+counter2Sz_];
750   tmp = counter1_;
751   for (i=0; i<counter1Sz_; i++) {
752     tmp->index = i;
753     counterNames[i] = tmp->arg; 
754     counterDesc[i] = tmp->desc;
755     tmp = tmp->next;
756   }
757   tmp = counter2_;
758   for (i=0; i<counter2Sz_; i++) {
759     tmp->index = counter1Sz_+i;
760     counterNames[counter1Sz_+i] = tmp->arg; 
761     counterDesc[counter1Sz_+i] = tmp->desc;
762     tmp = tmp->next;
763   }
764   CpvAccess(_counterNames) = counterNames;
765   CpvAccess(_counterDesc) = counterDesc;
766   CpvAccess(_numCounters) = numCounters;
767   // don't erase counterNames or counterDesc, 
768   // the reduction client will do it on the final reduction
769
770   _MEMCHECK(CpvAccess(_logPool));
771   CpvAccess(_logPool)->init(numCounters);
772   DEBUGF(("%d/%d DEBUG: Created _logPool at %08x\n", 
773           CmiMyPe(), CmiNumPes(), CpvAccess(_logPool)));
774 }
775
776 //! turn trace on/off, note that charm will automatically call traceBegin()
777 //! at the beginning of every run unless the command line option "+traceoff"
778 //! is specified
779 void TraceCounter::traceBegin() {
780   DEBUGF(("%d/%d traceBegin called\n", CmiMyPe(), CmiNumPes()));
781   if (traceOn_) { 
782       static bool print = true;
783       if (print) {
784         print = false;
785         if (CmiMyPe()==0) {
786           CmiPrintf("%d/%d WARN: traceBegin called but trace already on!\n"
787                     "            Sure you didn't mean to use +traceoff?\n",
788                     CmiMyPe(), CmiNumPes());
789         }
790     } 
791   }
792   else {
793     if (overview_) { beginOverview(); }
794     idleTime_ = 0.0;
795     if (writeByPhase_) { phase_++; }
796     traceOn_ = true;
797   }
798
799 #if ! CMK_TRACE_IN_CHARM
800   cancel_beginIdle = CcdCallOnConditionKeep(CcdPROCESSOR_BEGIN_IDLE,(CcdVoidFn)traceCounterBeginIdle,0);
801   cancel_endIdle = CcdCallOnConditionKeep(CcdPROCESSOR_BEGIN_BUSY,(CcdVoidFn)traceCounterEndIdle,0);
802 #endif
803 }
804
805 //! turn trace on/off, note that charm will automatically call traceBegin()
806 //! at the beginning of every run unless the command line option "+traceoff"
807 //! is specified
808 void TraceCounter::traceEnd() {
809   DEBUGF(("%d/%d traceEnd called\n", CmiMyPe(), CmiNumPes()));
810   if (!traceOn_) { 
811     static bool print = true;
812     if (print) {
813       print = false;
814       if (CmiMyPe()==0) {
815         CmiPrintf("%d/%d WARN: traceEnd called but trace not on!\n"
816                   "            Sure you didn't mean to use +traceoff?\n",
817                   CmiMyPe(), CmiNumPes());
818       }
819     }
820   }
821   else {
822     traceOn_ = false;
823     dirty_ = false;
824     if (overview_) { endOverview(); }  // overview switches counters automatic
825     else if (switchByPhase_) { switchCounters(); };
826
827     if (!noLog_ && writeByPhase_) {
828       if (CmiMyPe()==0) { CpvAccess(_logPool)->writeSts(phase_); }
829       CpvAccess(_logPool)->write(phase_); 
830     }
831     reductionPhase_++;
832     CpvAccess(_logPool)->doReduction(reductionPhase_, idleTime_); 
833     if (writeByPhase_) {
834       idleTime_ = 0.0;
835       CpvAccess(_logPool)->clearEps(); 
836     }
837     // setTrace must go after the writes otherwise the writes won't go through
838     DEBUGF(("%d/%d DEBUG: Created _logPool at %08x\n", 
839             CmiMyPe(), CmiNumPes(), CpvAccess(_logPool)));
840   }
841
842 #if ! CMK_TRACE_IN_CHARM
843   CcdCancelCallOnConditionKeep(CcdPROCESSOR_BEGIN_IDLE, cancel_beginIdle);
844   CcdCancelCallOnConditionKeep(CcdPROCESSOR_BEGIN_BUSY, cancel_endIdle);
845 #endif
846
847 }
848
849 //! begin/end execution of a Charm++ entry point
850 //! NOTE: begin/endPack and begin/endUnpack can be called in between
851 //!       a beginExecute and its corresponding endExecute.
852 void TraceCounter::beginExecute(envelope *e)
853 {
854   // no message means thread execution
855   if (e==NULL) { beginExecute(-1,-1,_threadEP,-1); }
856   else { beginExecute(-1,-1,e->getEpIdx(),-1); }  
857 }
858
859 //! begin/end execution of a Charm++ entry point
860 //! NOTE: begin/endPack and begin/endUnpack can be called in between
861 //!       a beginExecute and its corresponding endExecute.
862 void TraceCounter::beginExecute
863 (
864   int event,
865   int msgType,
866   int ep,
867   int srcPe, 
868   int mlen,
869   CmiObjId *idx
870 )
871 {
872   DEBUGF(("%d/%d DEBUG: beginExecute EP %d\n", CmiMyPe(), CmiNumPes(), ep));
873
874   if (!traceOn_ || overview_) { return; }
875
876   execEP_=ep;
877
878   if (status_!= IDLE) {
879     static bool print = true;
880     if (print) {
881       print = false;
882       if (CmiMyPe()==0) { 
883         CmiPrintf("WARN: %d beginExecute called when status not IDLE!\n", 
884                   CmiMyPe());
885       }
886     }
887     return;
888   }
889   else { status_=WORKING; }
890
891   if ((genStart_=start_counters(counter1_->code, counter2_->code))<0)
892   {
893     CmiPrintf("genStart=%d counter1=%d counter2=%d\n", 
894               genStart_, counter1_->code, counter2_->code);
895     CmiAbort("ERROR: start_counters() in beginExecute\n");
896   }
897
898   startEP_=TraceTimer();
899   DEBUGF(("%d/%d DEBUG:   beginExecute EP %d genStart %d\n", 
900           CmiMyPe(), CmiNumPes(), ep, genStart_));
901 }
902
903 //! begin/end execution of a Charm++ entry point
904 //! NOTE: begin/endPack and begin/endUnpack can be called in between
905 //!       a beginExecute and its corresponding endExecute.
906 void TraceCounter::endExecute()
907 {
908   DEBUGF(("%d/%d DEBUG: endExecute EP %d genStart_ %d\n", 
909           CmiMyPe(), CmiNumPes(), execEP_, genStart_));
910
911   if (!traceOn_ || overview_) { return; }
912
913   if (status_!=WORKING) {
914     static bool print = true;
915     if (print) {
916       print = false;
917       if (CmiMyPe()==0) {
918         CmiPrintf("WARN: %d endExecute called when status not WORKING!\n", 
919                   CmiMyPe());
920       }
921     }
922     return;
923   }
924   else { status_=IDLE; }
925
926   double t = TraceTimer();
927
928   long long value1 = 0, value2 = 0;
929   int genRead;
930   if ((genRead=read_counters(counter1_->code, &value1, counter2_->code, &value2)) < 0 ||
931       genRead != genStart_)
932   {
933     CmiPrintf("genRead %d genStart_ %d counter1 %ld counter2 %ld\n",
934               genRead, genStart_, value1, value2);
935     traceClose();
936     CmiAbort("ERROR: read_counters() in endExecute\n");
937   }
938
939   DEBUGF((
940     "%d/%d DEBUG:   endExecute genRead %d Time %f counter1 %d counter2 %ld\n", 
941     CmiMyPe(), CmiNumPes(), genRead, t-startEP_, value1, value2));
942   if (execEP_ != -1) { 
943     dirty_ = true;
944     CpvAccess(_logPool)->setEp(execEP_, counter1_->index, value1, 
945                                counter2_->index, value2, t-startEP_); 
946     if (!switchByPhase_) { switchCounters(); }
947   }
948 }
949
950 //! begin/end idle time for this pe
951 void TraceCounter::beginIdle() {
952   if (traceOn_) { startIdle_ = TraceTimer(); } 
953 }
954
955 //! begin/end idle time for this pe
956 void TraceCounter::endIdle() {
957   if (traceOn_) { idleTime_ += TraceTimer()-startIdle_; }
958 }
959
960 //! begin/end the process of packing a message (to send)
961 void TraceCounter::beginPack() 
962
963   DEBUGF(("%d/%d DEBUG: beginPack\n", CmiMyPe(), CmiNumPes()));
964
965   // beginPack/endPack can get called between beginExecute/endExecute 
966   // and can't have nested counter reads on certain architectures and on
967   // on those architectures the time to call stop/start_counters can be
968   // expensive
969 }
970
971 //! begin/end the process of packing a message (to send)
972 void TraceCounter::endPack() {
973   DEBUGF(("%d/%d DEBUG: endPack\n", CmiMyPe(), CmiNumPes()));
974
975   // beginPack/endPack can get called between beginExecute/endExecute 
976   // and can't have nested counter reads on certain architectures and on
977   // on those architectures the time to call stop/start_counters can be
978   // expensive
979 }
980
981 //! begin/end the process of unpacking a message (can occur before calling
982 //! a entry point or during an entry point when 
983 void TraceCounter::beginUnpack() { 
984   DEBUGF(("%d/%d DEBUG: beginUnpack\n", CmiMyPe(), CmiNumPes()));
985
986   // beginUnpack/endUnpack can get called between beginExecute/endExecute 
987   // and can't have nested counter reads on certain architectures and on
988   // on those architectures the time to call stop/start_counters can be
989   // expensive
990 }
991
992 //! begin/end the process of unpacking a message (can occur before calling
993 //! a entry point or during an entry point when 
994 void TraceCounter::endUnpack() {
995   DEBUGF(("%d/%d DEBUG: endUnpack\n", CmiMyPe(), CmiNumPes()));
996   
997   // beginUnpack/endUnpack can get called between beginExecute/endExecute 
998   // and can't have nested counter reads on certain architectures and on
999   // on those architectures the time to call stop/start_counters can be
1000   // expensive
1001 }
1002
1003 //! begin/end of execution
1004 void TraceCounter::beginComputation()
1005 {
1006 }
1007
1008 //! end computation, do a reduction here in hopes that it finishes before 
1009 //! traceClose called and the program exits
1010 void TraceCounter::endComputation() {
1011   CpvAccess(_logPool)->doReduction(-1, idleTime_); 
1012 }
1013
1014 //! clear all data collected for entry points
1015 void TraceCounter::traceClearEps() {
1016   CpvAccess(_logPool)->clearEps();
1017 }
1018
1019 //! write the summary sts file for this trace
1020 void TraceCounter::traceWriteSts() {
1021   if (traceOn_ && !noLog_) {
1022     if (CmiMyPe()==0) { CpvAccess(_logPool)->writeSts(); }
1023   }
1024 }
1025
1026 //! do any clean-up necessary for tracing
1027 void TraceCounter::traceClose()
1028 {
1029   if (dirty_) {
1030     if (overview_) { endOverview(); }
1031     else {
1032       if (!noLog_) {
1033         CpvAccess(_logPool)->write();
1034         if(CmiMyPe()==0) { 
1035           CpvAccess(_logPool)->writeSts(); 
1036           CmiPrintf("TraceCounter dirty, writing results\n");
1037         }
1038       }
1039     }
1040     dirty_ = false;
1041   }
1042   if (CpvAccess(_logPool)!=NULL) { 
1043     CkpvAccess(_trace)->endComputation();
1044     delete CpvAccess(_logPool);
1045     CpvAccess(_logPool) = NULL;
1046   }
1047 }
1048
1049 //! start/stop the overall counting ov eps (don't write to logCount, 
1050 //! just print to screen
1051 void TraceCounter::beginOverview()
1052 {
1053   DEBUGF(("%d/%d DEBUG:   beginOverview\n", 
1054           CmiMyPe(), CmiNumPes()));
1055   if ((genStart_=start_counters(counter1_->code, counter2_->code))<0)
1056   {
1057     CmiPrintf("genStart=%d counter1=%d counter2=%d\n", 
1058               genStart_, counter1_->code, counter2_->code);
1059     CmiAbort("ERROR: start_counters() in beginOverview\n");
1060   }
1061   startEP_=TraceTimer();
1062   DEBUGF(("%d/%d DEBUG:   beginOverview\n", CmiMyPe(), CmiNumPes()));
1063   dirty_ = true;
1064 }
1065
1066 void TraceCounter::endOverview()
1067 {
1068   DEBUGF(("%d/%d DEBUG:   endOverview\n", CmiMyPe(), CmiNumPes()));
1069  
1070   double t = TraceTimer();
1071   int _numEntries=_entryTable.size();
1072
1073   long long value1 = 0, value2 = 0;
1074   int genRead;
1075   if ((genRead=read_counters(counter1_->code, &value1, counter2_->code, &value2)) < 0 ||
1076       genRead != genStart_)
1077   {
1078     CmiPrintf("genRead %d genStart_ %d counter1 %ld counter2 %ld\n",
1079               genRead, genStart_, value1, value2);
1080     traceClose();
1081     CmiAbort("ERROR: read_counters() in endOverview\n");
1082   }
1083
1084   DEBUGF((
1085     "%d/%d DEBUG:   endOverview genRead %d Time %f counter1 %ld counter2 %ld\n", 
1086     CmiMyPe(), CmiNumPes(), genRead, t-startEP_, value1, value2));
1087   dirty_ = false;
1088
1089   CpvAccess(_logPool)->setEp(_numEntries, 
1090                              counter1_->index, value1, 
1091                              counter2_->index, value2, 
1092                              t-startEP_); 
1093   DEBUGF((
1094     "%d/%d OVERVIEW phase%d Time(us) %f %s %ld %s %ld Idle(us) %f"
1095     " (overflow? MAX=%ld)\n",
1096     CmiMyPe(), CmiNumPes(), phase_, (t-startEP_)*1e6, counter1_->arg, value1, 
1097     counter2_->arg, value2, idleTime_*1e6, MAXLONGLONG));
1098   // this is phase boundary anyway, so switch counters
1099   switchCounters(); 
1100 }
1101
1102 //! switch counters by whatever switching strategy 
1103 void TraceCounter::switchCounters()
1104 {
1105   static bool first = true;
1106   if (switchRandom_) {
1107     int i;
1108     if (first) { first = false;  srand(TraceTimer()*1e6); }
1109     int counter1Change = 
1110       (int) (rand() / (double) 0x7FFF * counter1Sz_ + 0.5);  // 2^15-1
1111     int counter2Change = 
1112       (int) (rand() / (double) 0x7FFF * counter2Sz_ + 0.5);  // 2^15-1
1113     if (CmiMyPe()==0) {
1114       DEBUGF(("change %d/%d %d/%d\n", 
1115               counter1Change, counter1Sz_, 
1116               counter2Change, counter2Sz_));
1117     }
1118     if (counter1Change > counter1Sz_) { counter1Change = counter1Sz_; }
1119     if (counter2Change > counter2Sz_) { counter2Change = counter2Sz_; }
1120     for (i=0; i<counter1Change; i++) { counter1_ = counter1_->next; }
1121     for (i=0; i<counter2Change; i++) { counter2_ = counter2_->next; }
1122   }
1123   else {
1124     counter1_ = counter1_->next;
1125     counter2_ = counter2_->next;
1126   }
1127   if (CmiMyPe()==0) {
1128     DEBUGF(("%d/%d New counters are %s %s\n", 
1129             CmiMyPe(), CmiNumPes(), counter1_->arg, counter2_->arg));
1130   }
1131 }
1132
1133 //! add the argument parameters to the linked list of args choices
1134 void TraceCounter::registerArg(CounterArg* arg)
1135 {
1136   if (firstArg_ == NULL) {
1137     firstArg_ = lastArg_ = arg;
1138     argStrSize_ = strlen(arg->arg);
1139   }
1140   else { 
1141     // check to see if any redundancy 
1142     CounterArg* check = firstArg_;
1143     while (check != NULL) {
1144       if (strcmp(check->arg, arg->arg)==0 || check->code == arg->code) {
1145         if (CmiMyPe()==0) { 
1146           CmiPrintf("Two args with same name %s or code %d\n", 
1147                     arg->arg, arg->code); 
1148         }
1149         CmiAbort("TraceCounter::registerArg()\n");
1150       }
1151       check = check->next;
1152     }
1153
1154     lastArg_->next = arg;
1155     lastArg_ = arg;
1156     int len = strlen(arg->arg);
1157     if (len > argStrSize_) { argStrSize_ = len; }
1158   }
1159 }
1160
1161 //! see if the arg (str or code) matches any in the linked list of choices
1162 //! and sets arg->code to the SGI code
1163 //! return true if arg matches, false otherwise
1164 bool TraceCounter::matchArg(CounterArg* arg)
1165 {
1166   bool match = false;                // will be set to true if arg matches
1167   CounterArg* matchArg = firstArg_;  // traverse linked list
1168   int matchCode = atoi(arg->arg);    // in case user specs num on commline
1169   if (matchCode == 0) {
1170     if (arg->arg[0] != '0' || arg->arg[1] != '\0') { matchCode = -1; }
1171   }
1172   // DEBUGF(("%d/%d DEBUG: Matching %s or %d\n", CmiMyPe(), CmiNumPes(), arg->arg, matchCode));
1173   while (matchArg != NULL && !match) {
1174     // DEBUGF(("%d/%d DEBUG:   Examining %d %s\n", CmiMyPe(), CmiNumPes(), matchArg->code, matchArg->arg));
1175     if (strcmp(matchArg->arg, arg->arg)==0) {
1176       match = true;
1177       arg->code = matchArg->code;
1178       arg->desc = matchArg->desc;
1179     }
1180     else if (matchArg->code == matchCode) {
1181       match = true;
1182       arg->code = matchArg->code;
1183       arg->arg = matchArg->arg;
1184       arg->desc = matchArg->desc;
1185     }
1186     matchArg = matchArg->next;
1187   }
1188   // DEBUGF(("%d/%d DEBUG: Match = %d\n", CmiMyPe(), CmiNumPes(), match));
1189   return match;
1190 }
1191
1192 //! print out usage argument
1193 void TraceCounter::usage() {
1194   CmiPrintf(
1195     "ERROR: You've linked with '-tracemode counter', so you must specify\n"
1196     "       the +counters <counters> option followed by any of the \n"
1197     "       following optional command line options.\n"
1198     "\n"
1199     "REQUIRED: +counters <counter>\n"
1200     "\n"
1201     "  +counters <counters>: Where <counters> is comma delimited list\n"
1202     "                        of valid counters.  Type '+count-help' to\n"
1203     "                        get a list of valid counters.\n"
1204     "\n"
1205     "OPTIONAL: [+count-overview] [+count-switchrandom] [+switchbyphase]\n"
1206     "\n"
1207     "  +count-overview:      Collect counter values between start/stop\n"
1208     "                        of the program (or traceBegin/traceEnd if\n"
1209     "                        user marked events are on [see Performance\n"
1210     "                        Counter section of the Charm++ manual]).\n"
1211     "                        Normal operation collects counter values\n"
1212     "                        between the stop/start of Charm++ entry\n"
1213     "                        points.\n"
1214     "  +count-switchrandom:  Counters will switch randomly between\n"
1215     "                        each event instead of in the order\n"
1216     "                        specified by the <counters> arg.\n"
1217     "  +count-switchbyphase: Counters will switch not every EP call,\n"
1218     "                        but only in between phases (between each\n"
1219     "                        traceBegin/traceEnd call).\n"
1220     "  +count-nolog:         Don't write any log files.\n"
1221     "\n"
1222     "See the Performance Counter section of the Charm++ manual for\n"
1223     "examples of different options.\n"
1224     "\n");
1225 }
1226
1227 //! print out all arguments in the linked-list of choices
1228 void TraceCounter::printHelp()
1229 {
1230   CmiPrintf(
1231     "Specify one of the following (code or str) after +counters:\n\n"
1232     "  code  str\n"
1233     "  ----  ---\n");
1234
1235   // create a format so that all the str line up 
1236   char format[64];
1237   snprintf(format, 64, "    %%2d  %%-%ds  %%s\n", argStrSize_);
1238
1239   CounterArg* help = firstArg_;
1240   while (help != NULL) {
1241     CmiPrintf(format, help->code, help->arg, help->desc);
1242     help = help->next;
1243   }
1244 }
1245
1246 #endif // CMK_ORIGIN2000
1247
1248 /*@}*/