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