quick implementation to make projections to have object id for array elements. last...
[charm.git] / src / ck-perf / trace-summary.C
1 /*****************************************************************************
2  * $Source$
3  * $Author$
4  * $Date$
5  * $Revision$
6  *****************************************************************************/
7
8 /**
9  * \addtogroup CkPerf
10 */
11 /*@{*/
12
13 #include "charm++.h"
14 #include "trace-summary.h"
15 #include "trace-summaryBOC.h"
16
17 #define DEBUGF(x)  // CmiPrintf x
18
19 #define VER   4.0
20
21 #define INVALIDEP     -2
22
23 CkpvStaticDeclare(TraceSummary*, _trace);
24 static int _numEvents = 0;
25 #define NUM_DUMMY_EPS 9
26 CkpvDeclare(double, binSize);
27 CkpvDeclare(double, version);
28
29 int sumonly = 0;
30 int sumDetail = 0;
31
32 /**
33   For each TraceFoo module, _createTraceFoo() must be defined.
34   This function is called in _createTraces() generated in moduleInit.C
35 */
36 void _createTracesummary(char **argv)
37 {
38   DEBUGF(("%d createTraceSummary\n", CkMyPe()));
39   CkpvInitialize(TraceSummary*, _trace);
40   CkpvAccess(_trace) = new  TraceSummary(argv);
41   CkpvAccess(_traces)->addTrace(CkpvAccess(_trace));
42 }
43
44
45 /// function call for starting a phase in trace summary logs 
46 extern "C" 
47 void CkSummary_StartPhase(int phase)
48 {
49    CkpvAccess(_trace)->startPhase(phase);
50 }
51
52
53 /// function call for adding an event mark
54 extern "C" 
55 void CkSummary_MarkEvent(int eventType)
56 {
57    CkpvAccess(_trace)->addEventType(eventType);
58 }
59
60
61 PhaseEntry::PhaseEntry() 
62 {
63   int _numEntries=_entryTable.size();
64   // FIXME: Hopes there won't be more than 10 more EP's registered from now on...
65   nEPs = _numEntries+10; 
66   count = new int[nEPs];
67   times = new double[nEPs];
68   maxtimes = new double[nEPs];
69   for (int i=0; i<nEPs; i++) {
70     count[i] = 0;
71     times[i] = 0.0;
72     maxtimes[i] = 0.;
73   }
74 }
75
76 SumLogPool::~SumLogPool() 
77 {
78   if (!sumonly) {
79   write();
80   fclose(fp);
81   if (sumDetail) fclose(sdfp);
82   }
83   // free memory for mark
84   if (markcount > 0)
85   for (int i=0; i<MAX_MARKS; i++) {
86     for (int j=0; j<events[i].length(); j++)
87       delete events[i][j];
88   }
89   delete[] pool;
90   delete[] epInfo;
91   delete[] cpuTime;
92   delete[] numExecutions;
93 }
94
95 void SumLogPool::addEventType(int eventType, double time)
96 {
97    if (eventType <0 || eventType >= MAX_MARKS) {
98        CkPrintf("Invalid event type %d!\n", eventType);
99        return;
100    }
101    MarkEntry *e = new MarkEntry;
102    e->time = time;
103    events[eventType].push_back(e);
104    markcount ++;
105 }
106
107 SumLogPool::SumLogPool(char *pgm) : numBins(0), phaseTab(MAX_PHASES) 
108 {
109    if (TRACE_CHARM_PE() == 0) return; // blue gene related
110
111    // TBD: Can this be moved to initMem?
112    poolSize = CkpvAccess(CtrLogBufSize);
113    if (poolSize % 2) poolSize++;        // make sure it is even
114    pool = new BinEntry[poolSize];
115    _MEMCHECK(pool);
116
117    fp = NULL;
118    sdfp = NULL;
119    //CmiPrintf("TRACE: %s:%d\n", fname, errno);
120    if (!sumonly) {
121     char pestr[10];
122     sprintf(pestr, "%d", CkMyPe());
123     int len = strlen(pgm) + strlen(".sumd.") + strlen(pestr) + 1;
124     char *fname = new char[len+1];
125
126     sprintf(fname, "%s.%s.sum", pgm, pestr);
127     do {
128       fp = fopen(fname, "w+");
129     } while (!fp && errno == EINTR);
130     if(!fp) {
131       CmiAbort("Cannot open Summary Trace File for writing...\n");
132     }
133
134     if (sumDetail) {
135         sprintf(fname, "%s.%s.sumd", pgm, pestr);
136         do {
137             sdfp = fopen(fname, "w+");
138         } while (!sdfp && errno == EINTR);
139         if(!sdfp) {
140             CmiAbort("Cannot open Detailed Summary Trace File for writing...\n");
141         }
142     }
143
144     delete[] fname;
145    }
146
147    // event
148    markcount = 0;
149
150    if (CkMyPe() == 0)
151    {
152     char *fname = new char[strlen(CkpvAccess(traceRoot))+strlen(".sum.sts")+1];
153     sprintf(fname, "%s.sum.sts", CkpvAccess(traceRoot));
154     stsfp = fopen(fname, "w+");
155     //CmiPrintf("File: %s \n", fname);
156     if(stsfp == 0)
157       CmiAbort("Cannot open summary sts file for writing.\n");
158     delete[] fname;
159    }
160 }
161
162 void SumLogPool::initMem()
163 {
164    int _numEntries=_entryTable.size();
165    epInfoSize = _numEntries + NUM_DUMMY_EPS + 1; // keep a spare EP
166    epInfo = new SumEntryInfo[epInfoSize];
167    _MEMCHECK(epInfo);
168
169    cpuTime = NULL;
170    numExecutions = NULL;
171    if (sumDetail) {
172        cpuTime = new double[poolSize*epInfoSize];
173        _MEMCHECK(cpuTime);
174        memset(cpuTime, 0, poolSize*epInfoSize*sizeof(double));
175        numExecutions = new int[poolSize*epInfoSize];
176        _MEMCHECK(numExecutions);
177        memset(numExecutions, 0, poolSize*epInfoSize*sizeof(int));
178
179 //         int i, e;
180 //         for(i=0; i<poolSize; i++) {
181 //             for(e=0; e< epInfoSize; e++) {
182 //                 setCPUtime(i,e,0.0);
183 //                 setNumExecutions(i,e,0);
184 //             }
185 //         }
186    }
187 }
188
189 int SumLogPool::getUtilization(int interval, int ep) {
190     return (int)(getCPUtime(interval, ep) * 100.0 / CkpvAccess(binSize)); 
191 };
192
193 void SumLogPool::write(void) 
194 {
195   int i;
196   unsigned int j;
197   int _numEntries=_entryTable.size();
198
199   fprintf(fp, "ver:%3.1f %d/%d count:%d ep:%d interval:%e", CkpvAccess(version), CkMyPe(), CkNumPes(), numBins, _numEntries, CkpvAccess(binSize));
200   if (CkpvAccess(version)>=3.0)
201   {
202     fprintf(fp, " phases:%d", phaseTab.numPhasesCalled());
203   }
204   fprintf(fp, "\n");
205
206   // write bin time
207 #if 1
208   int last=pool[0].getU();
209   pool[0].writeU(fp, last);
210   int count=1;
211   for(j=1; j<numBins; j++) {
212     int u = pool[j].getU();
213     if (last == u) {
214       count++;
215     }
216     else {
217       if (count > 1) fprintf(fp, "+%d", count);
218       pool[j].writeU(fp, u);
219       last = u;
220       count = 1;
221     }
222   }
223   if (count > 1) fprintf(fp, "+%d", count);
224 #else
225   for(j=0; j<numEntries; j++) 
226       pool[j].write(fp);
227 #endif
228   fprintf(fp, "\n");
229
230   // write entry execution time
231   fprintf(fp, "EPExeTime: ");
232   for (i=0; i<_numEntries; i++)
233     fprintf(fp, "%ld ", (long)(epInfo[i].epTime*1.0e6));
234   fprintf(fp, "\n");
235   // write entry function call times
236   fprintf(fp, "EPCallTime: ");
237   for (i=0; i<_numEntries; i++)
238     fprintf(fp, "%d ", epInfo[i].epCount);
239   fprintf(fp, "\n");
240   // write max entry function execute times
241   fprintf(fp, "MaxEPTime: ");
242   for (i=0; i<_numEntries; i++)
243     fprintf(fp, "%ld ", (long)(epInfo[i].epMaxTime*1.0e6));
244   fprintf(fp, "\n");
245 #if 0
246   for (i=0; i<SumEntryInfo::HIST_SIZE; i++) {
247     for (j=0; j<_numEntries; j++) 
248       fprintf(fp, "%d ", epInfo[j].hist[i]);
249     fprintf(fp, "\n");
250   }
251 #endif
252   // write marks
253   if (CkpvAccess(version)>=2.0) 
254   {
255     fprintf(fp, "NumMarks: %d ", markcount);
256     for (i=0; i<MAX_MARKS; i++) {
257       for(int j=0; j<events[i].length(); j++)
258         fprintf(fp, "%d %f ", i, events[i][j]->time);
259     }
260     fprintf(fp, "\n");
261   }
262   // write phases
263   if (CkpvAccess(version)>=3.0)
264   {
265     phaseTab.write(fp);
266   }
267
268   // write summary details
269   if (sumDetail) {
270         fprintf(sdfp, "ver:%3.1f cpu:%d/%d numIntervals:%d numEPs:%d intervalSize:%e\n",
271                 CkpvAccess(version), CkMyPe(), CkNumPes(),
272                 numBins, _numEntries, CkpvAccess(binSize));
273
274         // Write out cpuTime in microseconds
275         // Run length encoding (RLE) along EP axis
276         fprintf(sdfp, "ExeTimePerEPperInterval ");
277         unsigned int e, i;
278         long last= (long) (getCPUtime(0,0)*1.0e6);
279         int count=0;
280         fprintf(sdfp, "%ld", last);
281         for(e=0; e<_numEntries; e++) {
282             for(i=0; i<numBins; i++) {
283
284                 long u= (long) (getCPUtime(i,e)*1.0e6);
285                 if (last == u) {
286                     count++;
287                 } else {
288
289                     if (count > 1) fprintf(sdfp, "+%d", count);
290                     fprintf(sdfp, " %ld", u);
291                     last = u;
292                     count = 1;
293                 }
294             }
295         }
296         if (count > 1) fprintf(sdfp, "+%d", count);
297         fprintf(sdfp, "\n");
298
299         // Write out numExecutions
300         // Run length encoding (RLE) along EP axis
301         fprintf(sdfp, "EPCallTimePerInterval ");
302         last= getNumExecutions(0,0);
303         count=0;
304         fprintf(sdfp, "%d", last);
305         for(e=0; e<_numEntries; e++) {
306             for(i=0; i<numBins; i++) {
307
308                 long u= getNumExecutions(i, e);
309                 if (last == u) {
310                     count++;
311                 } else {
312
313                     if (count > 1) fprintf(sdfp, "+%d", count);
314                     fprintf(sdfp, " %d", u);
315                     last = u;
316                     count = 1;
317                 }
318             }
319         }
320         if (count > 1) fprintf(sdfp, "+%d", count);
321         fprintf(sdfp, "\n");
322   }
323 }
324
325 void SumLogPool::writeSts(void)
326 {
327   traceWriteSTS(stsfp,_numEvents);
328   for(int i=0;i<_numEvents;i++)
329     fprintf(stsfp, "EVENT %d Event%d\n", i, i);
330   fprintf(stsfp, "END\n");
331   fclose(stsfp);
332 }
333
334 // Called once per interval
335 void SumLogPool::add(double time, int pe) 
336 {
337   new (&pool[numBins++]) BinEntry(time);
338   if(poolSize==numBins) shrink();
339 }
340
341 // Called once per run of an EP
342 // adds 'time' to EP's time, increments epCount
343 void SumLogPool::setEp(int epidx, double time) 
344 {
345   if (epidx >= epInfoSize) {
346         CmiAbort("Invalid entry point!!\n");
347   }
348   //CmiPrintf("set EP: %d %e \n", epidx, time);
349   epInfo[epidx].setTime(time);
350   // set phase table counter
351   phaseTab.setEp(epidx, time);
352 }
353
354 // Called once from endExecute, endPack, etc. this function updates
355 // the sumDetail intervals.
356 void SumLogPool::updateSummaryDetail(int epIdx, double startTime, double endTime)
357 {
358         if (epIdx >= epInfoSize) {
359             CmiAbort("Too many entry points!!\n");
360         }
361
362         double binSz = CkpvAccess(binSize);
363         int startingBinIdx, endingBinIdx;
364         startingBinIdx = (int)(startTime/binSz);
365         endingBinIdx = (int)(endTime/binSz);
366         // shrink if needed
367         while (endingBinIdx >= poolSize) {
368           shrink();
369           CmiAssert(CkpvAccess(binSize) > binSz);
370           binSz = CkpvAccess(binSize);
371           startingBinIdx = (int)(startTime/binSz);
372           endingBinIdx = (int)(endTime/binSz);
373         }
374
375         if (startingBinIdx == endingBinIdx) {
376             addToCPUtime(startingBinIdx, epIdx, endTime - startTime);
377         } else if (startingBinIdx < endingBinIdx) { // EP spans intervals
378             addToCPUtime(startingBinIdx, epIdx, (startingBinIdx+1)*binSz - startTime);
379             while(++startingBinIdx < endingBinIdx)
380                 addToCPUtime(startingBinIdx, epIdx, binSz);
381             addToCPUtime(endingBinIdx, epIdx, endTime - endingBinIdx*binSz);
382         } else {
383             CmiAbort("Error: end time of EP is less than start time\n");
384         }
385
386         incNumExecutions(startingBinIdx, epIdx);
387 };
388
389 // Shrinks pool[], cpuTime[], and numExecutions[]
390 void SumLogPool::shrink(void)
391 {
392 //  double t = CmiWallTimer();
393
394   // We ensured earlier that poolSize is even; therefore now numBins
395   // == poolSize == even.
396   int entries = numBins/2;
397   for (int i=0; i<entries; i++)
398   {
399      pool[i].setTime(pool[i*2].getTime() + pool[i*2+1].getTime());
400      if (sumDetail)
401      for (int e=0; e < epInfoSize; e++) {
402          setCPUtime(i, e, getCPUtime(i*2, e) + getCPUtime(i*2+1, e));
403          setNumExecutions(i, e, getNumExecutions(i*2, e) + getNumExecutions(i*2+1, e));
404      }
405   }
406   // zero out the remaining intervals
407   if (sumDetail) {
408     memset(&cpuTime[entries*epInfoSize], 0, (numBins-entries)*epInfoSize*sizeof(double));
409     memset(&numExecutions[entries*epInfoSize], 0, (numBins-entries)*epInfoSize*sizeof(int));
410   }
411   numBins = entries;
412   CkpvAccess(binSize) *= 2;
413
414 //CkPrintf("Shrinked binsize: %f entries:%d takes %fs!!!!\n", CkpvAccess(binSize), numEntries, CmiWallTimer()-t);
415 }
416
417 int  BinEntry::getU() { 
418   return (int)(time * 100.0 / CkpvAccess(binSize)); 
419 }
420
421 void BinEntry::write(FILE* fp)
422 {
423   int per = (int)(time * 100.0 / CkpvAccess(binSize));
424   fprintf(fp, "%4d", per);
425 }
426
427 void BinEntry::writeU(FILE* fp, int u)
428 {
429   fprintf(fp, "%4d", u);
430 }
431
432 TraceSummary::TraceSummary(char **argv):curevent(0),binStart(0.0),bin(0.0),msgNum(0)
433 {
434   char *tmpStr;
435   CkpvInitialize(double, binSize);
436   CkpvInitialize(double, version);
437   CkpvAccess(binSize) = BIN_SIZE;
438   CkpvAccess(version) = VER;
439   CmiGetArgDoubleDesc(argv,"+binsize",&CkpvAccess(binSize),
440         "CPU usage log time resolution");
441   CmiGetArgDoubleDesc(argv,"+version",&CkpvAccess(version),
442         "Write this .sum file version");
443
444   epThreshold = 0.001; 
445   CmiGetArgDoubleDesc(argv,"+epThreshold",&epThreshold,
446         "Execution time histogram lower bound");
447   epInterval = 0.001; 
448   CmiGetArgDoubleDesc(argv,"+epInterval",&epInterval,
449         "Execution time histogram bin size");
450
451   sumonly = CmiGetArgFlagDesc(argv, "+sumonly", "merge histogram bins on processor 0");
452   // +sumonly overrides +sumDetail
453   if (!sumonly)
454       sumDetail = CmiGetArgFlagDesc(argv, "+sumDetail", "more detailed summary info");
455
456   _logPool = new SumLogPool(CkpvAccess(traceRoot));
457   execEp=INVALIDEP;
458 }
459
460 void TraceSummary::traceClearEps(void)
461 {
462   _logPool->clearEps();
463 }
464
465 void TraceSummary::traceWriteSts(void)
466 {
467   if(CkMyPe()==0)
468       _logPool->writeSts();
469 }
470
471 void TraceSummary::traceClose(void)
472 {
473   if(CkMyPe()==0)
474       _logPool->writeSts();
475   if (TRACE_CHARM_PE()) {
476     CkpvAccess(_trace)->endComputation();
477     // destructor call the write()
478     delete _logPool;
479     // remove myself from traceArray so that no tracing will be called.
480     CkpvAccess(_traces)->removeTrace(this);
481   }
482 }
483
484 void TraceSummary::beginExecute(CmiObjId *tid)
485 {
486   beginExecute(-1,-1,_threadEP,-1);
487 }
488
489 void TraceSummary::beginExecute(envelope *e)
490 {
491   // no message means thread execution
492   if (e==NULL) {
493     beginExecute(-1,-1,_threadEP,-1);
494   }
495   else {
496     beginExecute(-1,-1,e->getEpIdx(),-1);
497   }  
498 }
499
500 void TraceSummary::beginExecute(int event,int msgType,int ep,int srcPe, int mlen, CmiObjId *idx)
501 {
502   if (execEp != INVALIDEP) {
503     CmiPrintf("Warning: TraceSummary two consecutive BEGIN_PROCESSING!\n");
504     return;
505   }
506
507   execEp=ep;
508   double t = TraceTimer();
509 //CmiPrintf("start: %f \n", start);
510
511   start = t;
512   double ts = binStart;
513   // fill gaps
514   while ((ts = ts + CkpvAccess(binSize)) < t)
515   {
516      _logPool->add(bin, CkMyPe());
517      bin=0.0;
518      binStart = ts;
519   }
520 }
521
522 void TraceSummary::endExecute(void)
523 {
524 //  if (!flag) return;
525   double t = TraceTimer();
526   double ts = start;
527   double nts = binStart;
528
529   if (execEp == INVALIDEP) {
530     CmiPrintf("Warning: TraceSummary END_PROCESSING without BEGIN_PROCESSING!\n");
531     return;
532   }
533
534   if (execEp != -1)
535   {
536     _logPool->setEp(execEp, t-ts);
537   }
538
539   while ((nts = nts + CkpvAccess(binSize)) < t)
540   {
541      bin += nts-ts;
542      binStart  = nts;
543      _logPool->add(bin, CkMyPe()); // This calls shrink() if needed
544      bin = 0;
545      ts = nts;
546   }
547   bin += t - ts;
548
549   if (sumDetail)
550       _logPool->updateSummaryDetail(execEp, start, t);
551
552   execEp = INVALIDEP;
553 }
554
555 void TraceSummary::beginPack(void)
556 {
557     packstart = CmiWallTimer();
558 }
559
560 void TraceSummary::endPack(void)
561 {
562     _logPool->setEp(_packEP, CmiWallTimer() - packstart);
563     if (sumDetail)
564         _logPool->updateSummaryDetail(_packEP,  TraceTimer(packstart), TraceTimer(CmiWallTimer()));
565 }
566
567 void TraceSummary::beginUnpack(void)
568 {
569     unpackstart = CmiWallTimer();
570 }
571
572 void TraceSummary::endUnpack(void)
573 {
574     _logPool->setEp(_unpackEP, CmiWallTimer()-unpackstart);
575     if (sumDetail)
576         _logPool->updateSummaryDetail(_unpackEP,  TraceTimer(unpackstart), TraceTimer(CmiWallTimer()));
577 }
578
579 void TraceSummary::beginComputation(void)
580 {
581   // initialze arrays because now the number of entries is known.
582   _logPool->initMem();
583 }
584
585 void TraceSummary::endComputation(void)
586 {
587   static int done = 0;
588   if (done) return;
589   done = 1;
590   if (msgNum==0) {
591 //CmiPrintf("Add at last: %d pe:%d time:%f msg:%d\n", index, CkMyPe(), bin, msgNum);
592      _logPool->add(bin, CkMyPe());
593      bin = 0.0;
594      msgNum ++;
595
596      binStart  += CkpvAccess(binSize);
597      double t = TraceTimer();
598      double ts = binStart;
599      while (ts < t)
600      {
601        _logPool->add(bin, CkMyPe());
602        bin=0.0;
603        ts += CkpvAccess(binSize);
604      }
605
606   }
607 }
608
609 void TraceSummary::addEventType(int eventType)
610 {
611   _logPool->addEventType(eventType, TraceTimer());
612 }
613
614 void TraceSummary::startPhase(int phase)
615 {
616    _logPool->startPhase(phase);
617 }
618
619
620 /// for TraceSummaryBOC
621
622 CkGroupID traceSummaryGID;
623
624 void TraceSummaryBOC::askSummary()
625 {
626   if (CkpvAccess(_trace) == NULL) return;
627
628 #if 0
629 #if CMK_TRACE_IN_CHARM
630   TRACE_CHARM_PE() = 0;
631 #endif
632 #endif
633
634   int n=0;
635   BinEntry *bins = NULL;
636   int traced = TRACE_CHARM_PE();
637   if (traced) {
638   CkpvAccess(_trace)->endComputation();
639   n = CkpvAccess(_trace)->pool()->getNumEntries();
640   bins = CkpvAccess(_trace)->pool()->bins();
641 #if 1
642   CmiPrintf("askSummary on [%d] numEntried=%d\n", CkMyPe(), n);
643 #if 0
644   for (int i=0; i<n; i++) 
645     CmiPrintf("%4d", bins[i].getU());
646   CmiPrintf("\n");
647 #endif
648 #endif
649   }
650   CProxy_TraceSummaryBOC p(traceSummaryGID);
651   p[0].sendSummaryBOC(traced, n, bins);
652 }
653
654 extern "C" void _CkExit();
655
656 void TraceSummaryBOC::sendSummaryBOC(int traced, int n, BinEntry *b)
657 {
658   int i;
659   if (CkpvAccess(_trace) == NULL) return;
660
661   CkAssert(CkMyPe() == 0);
662
663 #if 0
664 #if CMK_TRACE_IN_CHARM
665   TRACE_CHARM_PE() = 0;
666 #endif
667 #endif
668
669   count ++;
670   if (bins == NULL) {
671     nBins = CkpvAccess(_trace)->pool()->getNumEntries();
672     bins = new BinEntry[nBins];
673   }
674   if (traced) {
675     nTracedPEs ++;
676     if (n>nBins) n = nBins;
677     for (i=0; i<n; i++) {
678       bins[i].Time() += b[i].Time();
679     }
680   }
681   if (count == CkNumPes()) {
682     write();
683     _CkExit();
684   }
685 }
686
687 void TraceSummaryBOC::write(void) 
688 {
689   int i;
690   unsigned int j;
691   int _numEntries=_entryTable.size();
692
693   char *fname = new char[strlen(CkpvAccess(traceRoot))+strlen(".sum")+1];
694   sprintf(fname, "%s.sum", CkpvAccess(traceRoot));
695   FILE *sumfp = fopen(fname, "w+");
696   //CmiPrintf("File: %s \n", fname);
697   if(sumfp == 0)
698       CmiAbort("Cannot open summary sts file for writing.\n");
699   delete[] fname;
700
701   fprintf(sumfp, "ver:%3.1f %d/%d count:%d ep:%d interval:%e numTracedPE:%d", CkpvAccess(version), CkMyPe(), CkNumPes(), nBins, _numEntries, CkpvAccess(binSize), nTracedPEs);
702   fprintf(sumfp, "\n");
703
704   // write bin time
705 #if 0
706   int last=pool[0].getU();
707   pool[0].writeU(fp, last);
708   int count=1;
709   for(j=1; j<numEntries; j++) {
710     int u = pool[j].getU();
711     if (last == u) {
712       count++;
713     }
714     else {
715       if (count > 1) fprintf(fp, "+%d", count);
716       pool[j].writeU(fp, u);
717       last = u;
718       count = 1;
719     }
720   }
721   if (count > 1) fprintf(fp, "+%d", count);
722 #else
723   for(j=0; j<nBins; j++) {
724     bins[j].Time() /= nTracedPEs;
725     bins[j].write(sumfp);
726   }
727 #endif
728   fprintf(sumfp, "\n");
729   fclose(sumfp);
730
731 }
732
733 extern "C" void CombineSummary()
734 {
735 //CmiPrintf("[%d] CombineSummary called!\n", CkMyPe());
736   if (sumonly) {
737 CmiPrintf("[%d] Sum Only start!\n", CkMyPe());
738     CProxy_TraceSummaryBOC(traceSummaryGID).askSummary();
739   }
740   else CkExit();
741 }
742
743 void initTraceSummaryBOC()
744 {
745 #ifdef __BLUEGENE__
746   if(BgNodeRank()==0) {
747 #else
748   if (CkMyRank() == 0) {
749 #endif
750     registerExitFn(CombineSummary);
751   }
752 }
753
754 #include "TraceSummary.def.h"
755
756
757 /*@}*/