Added support to trace communication thread in SMP mode. To enable this, compile...
[charm.git] / src / ck-perf / trace-projections.C
1 /*****************************************************************************
2  * $Source$
3  * $Author$
4  * $Date$
5  * $Revision$
6  *****************************************************************************/
7
8
9 /**
10  * \addtogroup CkPerf
11 */
12 /*@{*/
13
14 #include <string.h>
15
16 #include "charm++.h"
17 #include "trace-projections.h"
18 #include "trace-projectionsBOC.h"
19
20 #define DEBUGF(x)           // CmiPrintf x
21
22 #define DefaultLogBufSize      1000000
23
24 // **CW** Simple delta encoding implementation
25 // delta encoding is on by default. It may be turned off later in
26 // the runtime.
27 int deltaLog;
28 int nonDeltaLog;
29
30 int checknested=0;              // check illegal nested begin/end execute 
31
32 CkGroupID traceProjectionsGID;
33
34 CkpvStaticDeclare(TraceProjections*, _trace);
35 CtvStaticDeclare(int,curThreadEvent);
36
37 CkpvDeclare(bool, useOutlierAnalysis);
38 CkpvDeclare(int, CtrLogBufSize);
39
40 typedef CkVec<char *>  usrEventVec;
41 CkpvStaticDeclare(usrEventVec, usrEventlist);
42 class UsrEvent {
43 public:
44   int e;
45   char *str;
46   UsrEvent(int _e, char* _s): e(_e),str(_s) {}
47 };
48 CkpvStaticDeclare(CkVec<UsrEvent *>*, usrEvents);
49
50
51
52 /// Disable the outputting of the trace logs
53 void disableTraceLogOutput()
54
55   CkpvAccess(_trace)->setWriteData(false);
56 }
57
58 /// Enable the outputting of the trace logs
59 void enableTraceLogOutput()
60 {
61   CkpvAccess(_trace)->setWriteData(true);
62 }
63
64
65
66 #ifdef CMK_OPTIMIZE
67 static int warned=0;
68 #define OPTIMIZED_VERSION       \
69         if (!warned) { warned=1;        \
70         CmiPrintf("\n\n!!!! Warning: traceUserEvent not available in optimized version!!!!\n\n\n"); }
71 #else
72 #define OPTIMIZED_VERSION /*empty*/
73 #endif
74
75 /*
76 On T3E, we need to have file number control by open/close files only when needed.
77 */
78 #if CMK_TRACE_LOGFILE_NUM_CONTROL
79   #define OPEN_LOG openLog("a");
80   #define CLOSE_LOG closeLog();
81 #else
82   #define OPEN_LOG
83   #define CLOSE_LOG
84 #endif
85
86 #if CMK_HAS_COUNTER_PAPI
87 int numPAPIEvents = 2;
88 int papiEvents[] = { PAPI_L3_DCM, PAPI_FP_OPS };
89 char *papiEventNames[] = {"PAPI_L3_DCM", "PAPI_FP_OPS"};
90 #endif
91
92 /**
93   For each TraceFoo module, _createTraceFoo() must be defined.
94   This function is called in _createTraces() generated in moduleInit.C
95 */
96 void _createTraceprojections(char **argv)
97 {
98   DEBUGF(("%d createTraceProjections\n", CkMyPe()));
99   CkpvInitialize(CkVec<char *>, usrEventlist);
100   CkpvInitialize(CkVec<UsrEvent *>*, usrEvents);
101   CkpvAccess(usrEvents) = new CkVec<UsrEvent *>();
102 #if CMK_BLUEGENE_CHARM
103   // CthRegister does not call the constructor
104 //  CkpvAccess(usrEvents) = CkVec<UsrEvent *>();
105 #endif
106   CkpvInitialize(TraceProjections*, _trace);
107   CkpvAccess(_trace) = new  TraceProjections(argv);
108   CkpvAccess(_traces)->addTrace(CkpvAccess(_trace));
109 }
110  
111 /* ****** CW TEMPORARY LOCATION ***** Support for thread listeners */
112
113 struct TraceThreadListener {
114   struct CthThreadListener base;
115   int event;
116   int msgType;
117   int ep;
118   int srcPe;
119   int ml;
120   CmiObjId idx;
121 };
122
123
124 extern "C"
125 void traceThreadListener_suspend(struct CthThreadListener *l)
126 {
127   TraceThreadListener *a=(TraceThreadListener *)l;
128   /* here, we activate the appropriate trace codes for the appropriate
129      registered modules */
130   traceSuspend();
131 }
132
133 extern "C"
134 void traceThreadListener_resume(struct CthThreadListener *l) 
135 {
136   TraceThreadListener *a=(TraceThreadListener *)l;
137   /* here, we activate the appropriate trace codes for the appropriate
138      registered modules */
139   _TRACE_BEGIN_EXECUTE_DETAILED(a->event,a->msgType,a->ep,a->srcPe,a->ml,
140                                 CthGetThreadID(a->base.thread));
141   a->event=-1;
142   a->srcPe=CkMyPe(); /* potential lie to migrated threads */
143   a->ml=0;
144 }
145
146 extern "C"
147 void traceThreadListener_free(struct CthThreadListener *l) 
148 {
149   TraceThreadListener *a=(TraceThreadListener *)l;
150   delete a;
151 }
152
153 void TraceProjections::traceAddThreadListeners(CthThread tid, envelope *e)
154 {
155 #ifndef CMK_OPTIMIZE
156   /* strip essential information from the envelope */
157   TraceThreadListener *a= new TraceThreadListener;
158   
159   a->base.suspend=traceThreadListener_suspend;
160   a->base.resume=traceThreadListener_resume;
161   a->base.free=traceThreadListener_free;
162   a->event=e->getEvent();
163   a->msgType=e->getMsgtype();
164   a->ep=e->getEpIdx();
165   a->srcPe=e->getSrcPe();
166   a->ml=e->getTotalsize();
167
168   CthAddListener(tid, (CthThreadListener *)a);
169 #endif
170 }
171
172 void LogPool::openLog(const char *mode)
173 {
174 #if CMK_PROJECTIONS_USE_ZLIB
175   if(compressed) {
176     if (nonDeltaLog) {
177       do {
178         zfp = gzopen(fname, mode);
179       } while (!zfp && (errno == EINTR || errno == EMFILE));
180       if(!zfp) CmiAbort("Cannot open Projections Compressed Non Delta Trace File for writing...\n");
181     }
182     if (deltaLog) {
183       do {
184         deltazfp = gzopen(dfname, mode);
185       } while (!deltazfp && (errno == EINTR || errno == EMFILE));
186       if (!deltazfp) 
187         CmiAbort("Cannot open Projections Compressed Delta Trace File for writing...\n");
188     }
189   } else {
190     if (nonDeltaLog) {
191       do {
192         fp = fopen(fname, mode);
193       } while (!fp && (errno == EINTR || errno == EMFILE));
194       if (!fp) {
195         CkPrintf("[%d] Attempting to open file [%s]\n",CkMyPe(),fname);
196         CmiAbort("Cannot open Projections Non Delta Trace File for writing...\n");
197       }
198     }
199     if (deltaLog) {
200       do {
201         deltafp = fopen(dfname, mode);
202       } while (!deltafp && (errno == EINTR || errno == EMFILE));
203       if (!deltafp) 
204         CmiAbort("Cannot open Projections Delta Trace File for writing...\n");
205     }
206   }
207 #else
208   if (nonDeltaLog) {
209     do {
210       fp = fopen(fname, mode);
211     } while (!fp && (errno == EINTR || errno == EMFILE));
212     if (!fp) {
213       CkPrintf("[%d] Attempting to open file [%s]\n",CkMyPe(),fname);
214       CmiAbort("Cannot open Projections Non Delta Trace File for writing...\n");
215     }
216   }
217   if (deltaLog) {
218     do {
219       deltafp = fopen(dfname, mode);
220     } while (!deltafp && (errno == EINTR || errno == EMFILE));
221     if(!deltafp) 
222       CmiAbort("Cannot open Projections Delta Trace File for writing...\n");
223   }
224 #endif
225 }
226
227 void LogPool::closeLog(void)
228 {
229 #if CMK_PROJECTIONS_USE_ZLIB
230   if(compressed) {
231     if (nonDeltaLog) gzclose(zfp);
232     if (deltaLog) gzclose(deltazfp);
233     return;
234   }
235 #endif
236   if (nonDeltaLog) { 
237 #if !defined(_WIN32) || defined(__CYGWIN__)
238     fsync(fileno(fp)); 
239 #endif
240     fclose(fp); 
241   }
242   if (deltaLog)  { 
243 #if !defined(_WIN32) || defined(__CYGWIN__)
244     fsync(fileno(deltafp)); 
245 #endif
246     fclose(deltafp);  
247   }
248 }
249
250 LogPool::LogPool(char *pgm) {
251   pool = new LogEntry[CkpvAccess(CtrLogBufSize)];
252   // defaults to writing data (no outlier changes)
253   writeData = true;
254   numEntries = 0;
255   // **CW** for simple delta encoding
256   prevTime = 0.0;
257   timeErr = 0.0;
258   globalEndTime = 0.0;
259   headerWritten = 0;
260   fileCreated = false;
261   poolSize = CkpvAccess(CtrLogBufSize);
262   pgmname = new char[strlen(pgm)+1];
263   strcpy(pgmname, pgm);
264 }
265
266 void LogPool::createFile(const char *fix)
267 {
268   if (fileCreated) {
269     return;
270   }
271   char pestr[10];
272   sprintf(pestr, "%d", CkMyPe());
273 #if CMK_PROJECTIONS_USE_ZLIB
274   int len;
275   if(compressed)
276     len = strlen(pgmname)+strlen(fix)+strlen(".logold")+strlen(pestr)+strlen(".gz")+3;
277   else
278     len = strlen(pgmname)+strlen(fix)+strlen(".logold")+strlen(pestr)+3;
279 #else
280   int len = strlen(pgmname)+strlen(fix)+strlen(".logold")+strlen(pestr)+3;
281 #endif
282   if (nonDeltaLog) {
283     fname = new char[len];
284   }
285   if (deltaLog) {
286     dfname = new char[len];
287   }
288 #if CMK_PROJECTIONS_USE_ZLIB
289   if(compressed) {
290     if (deltaLog && nonDeltaLog) {
291       sprintf(fname, "%s%s.%s.logold.gz", pgmname, fix, pestr);
292       sprintf(dfname, "%s%s.%s.log.gz", pgmname, fix, pestr);
293     } else {
294       if (nonDeltaLog) {
295         sprintf(fname, "%s%s.%s.log.gz", pgmname, fix, pestr);
296       } else {
297         sprintf(dfname, "%s%s.%s.log.gz", pgmname, fix, pestr);
298       }
299     }
300   } else {
301     if (deltaLog && nonDeltaLog) {
302       sprintf(fname, "%s%s.%s.logold", pgmname, fix, pestr);
303       sprintf(dfname, "%s%s.%s.log", pgmname, fix, pestr);
304     } else {
305       if (nonDeltaLog) {
306         sprintf(fname, "%s%s.%s.log", pgmname, fix, pestr);
307       } else {
308         sprintf(dfname, "%s%s.%s.log", pgmname, fix, pestr);
309       }
310     }
311   }
312 #else
313   if (deltaLog && nonDeltaLog) {
314     sprintf(fname, "%s%s.%s.logold", pgmname, fix, pestr);
315     sprintf(dfname, "%s%s.%s.log", pgmname, fix, pestr);
316   } else {
317     if (nonDeltaLog) {
318       sprintf(fname, "%s%s.%s.log", pgmname, fix, pestr);
319     } else {
320       sprintf(dfname, "%s%s.%s.log", pgmname, fix, pestr);
321     }
322   }
323 #endif
324   fileCreated = true;
325   openLog("w+");
326   CLOSE_LOG 
327 }
328
329 void LogPool::createSts(const char *fix)
330 {
331   CkAssert(CkMyPe() == 0);
332   // create the sts file
333   char *fname = new char[strlen(CkpvAccess(traceRoot))+strlen(fix)+strlen(".sts")+2];
334   sprintf(fname, "%s%s.sts", CkpvAccess(traceRoot), fix);
335   do
336     {
337       stsfp = fopen(fname, "w");
338     } while (!stsfp && (errno == EINTR || errno == EMFILE));
339   if(stsfp==0)
340     CmiAbort("Cannot open projections sts file for writing.\n");
341   delete[] fname;
342 }  
343
344 void LogPool::createRC()
345 {
346   // create the projections rc file.
347   fname = 
348     new char[strlen(CkpvAccess(traceRoot))+strlen(".projrc")+1];
349   sprintf(fname, "%s.projrc", CkpvAccess(traceRoot));
350   do {
351     rcfp = fopen(fname, "w");
352   } while (!rcfp && (errno == EINTR || errno == EMFILE));
353   if (rcfp==0) {
354     CmiAbort("Cannot open projections configuration file for writing.\n");
355   }
356   delete[] fname;
357 }
358
359 LogPool::~LogPool() 
360 {
361   if (writeData) {
362     writeLog();
363 #if !CMK_TRACE_LOGFILE_NUM_CONTROL
364     closeLog();
365 #endif
366   }
367
368 #if CMK_BLUEGENE_CHARM
369   extern int correctTimeLog;
370   if (correctTimeLog) {
371     createFile("-bg");
372     if (CkMyPe() == 0) {
373       createSts("-bg");
374     }
375     writeHeader();
376     if (CkMyPe() == 0) writeSts(NULL);
377     postProcessLog();
378   }
379 #endif
380
381   delete[] pool;
382   delete [] fname;
383 }
384
385 void LogPool::writeHeader()
386 {
387   if (headerWritten) return;
388   headerWritten = 1;
389   if(!binary) {
390 #if CMK_PROJECTIONS_USE_ZLIB
391     if(compressed) {
392       if (nonDeltaLog) {
393         gzprintf(zfp, "PROJECTIONS-RECORD %d\n", numEntries);
394       }
395       if (deltaLog) {
396         gzprintf(deltazfp, "PROJECTIONS-RECORD %d DELTA\n", numEntries);
397       }
398     } 
399     else /* else clause is below... */
400 #endif
401     /*... may hang over from else above */ {
402       if (nonDeltaLog) {
403         fprintf(fp, "PROJECTIONS-RECORD %d\n", numEntries);
404       }
405       if (deltaLog) {
406         fprintf(deltafp, "PROJECTIONS-RECORD %d DELTA\n", numEntries);
407       }
408     }
409   }
410   else { // binary
411       if (nonDeltaLog) {
412         fwrite(&numEntries,sizeof(numEntries),1,fp);
413       }
414       if (deltaLog) {
415         fwrite(&numEntries,sizeof(numEntries),1,deltafp);
416       }
417   }
418 }
419
420 void LogPool::writeLog(void)
421 {
422   createFile();
423   OPEN_LOG
424   writeHeader();
425   if (nonDeltaLog) write(0);
426   if (deltaLog) write(1);
427   CLOSE_LOG
428 }
429
430 void LogPool::write(int writedelta) 
431 {
432   // **CW** Simple delta encoding implementation
433   // prevTime has to be maintained as an object variable because
434   // LogPool::write may be called several times depending on the
435   // +logsize value.
436   PUP::er *p = NULL;
437   if (binary) {
438     p = new PUP::toDisk(writedelta?deltafp:fp);
439   }
440 #if CMK_PROJECTIONS_USE_ZLIB
441   else if (compressed) {
442     p = new toProjectionsGZFile(writedelta?deltazfp:zfp);
443   }
444 #endif
445   else {
446     p = new toProjectionsFile(writedelta?deltafp:fp);
447   }
448   CmiAssert(p);
449   for(UInt i=0; i<numEntries; i++) {
450     if (!writedelta) {
451       pool[i].pup(*p);
452     }
453     else {      // delta
454       double time = pool[i].time;
455       if (pool[i].type != BEGIN_COMPUTATION && pool[i].type != END_COMPUTATION)
456       {
457         double timeDiff = (time-prevTime)*1.0e6;
458         UInt intTimeDiff = (UInt)timeDiff;
459         timeErr += timeDiff - intTimeDiff; /* timeErr is never >= 2.0 */
460         if (timeErr > 1.0) {
461           timeErr -= 1.0;
462           intTimeDiff++;
463         }
464         pool[i].time = intTimeDiff/1.0e6;
465       }
466       pool[i].pup(*p);
467       pool[i].time = time;      // restore time value
468       prevTime = time;
469     }
470   }
471   delete p;
472 }
473
474 void LogPool::writeSts(void)
475 {
476   // for whining compilers
477   int i;
478   char name[30];
479   // generate an automatic unique ID for each log
480   fprintf(stsfp, "PROJECTIONS_ID %s\n", "");
481   fprintf(stsfp, "VERSION %s\n", PROJECTION_VERSION);
482 #if CMK_HAS_COUNTER_PAPI
483   fprintf(stsfp, "TOTAL_PAPI_EVENTS %d\n", numPAPIEvents);
484   // for now, use i, next time use papiEvents[i].
485   // **CW** papi event names is a hack.
486   for (i=0;i<numPAPIEvents;i++) {
487     fprintf(stsfp, "PAPI_EVENT %d %s\n", i, papiEventNames[i]);
488   }
489 #endif
490   traceWriteSTS(stsfp,CkpvAccess(usrEvents)->length());
491   for(i=0;i<CkpvAccess(usrEvents)->length();i++){
492     fprintf(stsfp, "EVENT %d %s\n", (*CkpvAccess(usrEvents))[i]->e, (*CkpvAccess(usrEvents))[i]->str);
493   }     
494 }
495
496 void LogPool::writeSts(TraceProjections *traceProj){
497   writeSts();
498   if (traceProj != NULL) {
499     CkHashtableIterator  *funcIter = traceProj->getfuncIterator();
500     funcIter->seekStart();
501     int numFuncs = traceProj->getFuncNumber();
502     fprintf(stsfp,"TOTAL_FUNCTIONS %d \n",numFuncs);
503     while(funcIter->hasNext()) {
504       StrKey *key;
505       int *obj = (int *)funcIter->next((void **)&key);
506       fprintf(stsfp,"FUNCTION %d %s \n",*obj,key->getStr());
507     }
508   }
509   fprintf(stsfp, "END\n");
510   fclose(stsfp);
511 }
512
513 void LogPool::writeRC(void)
514 {
515   CkAssert(CkMyPe() == 0);
516   fprintf(rcfp,"RC_GLOBAL_END_TIME %lld\n",
517           (CMK_TYPEDEF_UINT8)(1.0e6*globalEndTime));
518   if (CkpvAccess(useOutlierAnalysis)) {
519     fprintf(rcfp,"RC_OUTLIER_FILTERED true\n");
520   } else {
521     fprintf(rcfp,"RC_OUTLIER_FILTERED false\n");
522   }       
523   fclose(rcfp);
524 }
525
526
527 #if CMK_BLUEGENE_CHARM
528 static void updateProjLog(void *data, double t, double recvT, void *ptr)
529 {
530   LogEntry *log = (LogEntry *)data;
531   FILE *fp = *(FILE **)ptr;
532   log->time = t;
533   log->recvTime = recvT<0.0?0:recvT;
534 //  log->write(fp);
535   toProjectionsFile p(fp);
536   log->pup(p);
537 }
538 #endif
539
540 // flush log entries to disk
541 void LogPool::flushLogBuffer()
542 {
543   if (numEntries) {
544     double writeTime = TraceTimer();
545     writeLog();
546     numEntries = 0;
547     new (&pool[numEntries++]) LogEntry(writeTime, BEGIN_INTERRUPT);
548     new (&pool[numEntries++]) LogEntry(TraceTimer(), END_INTERRUPT);
549   }
550 }
551
552 void LogPool::add(UChar type, UShort mIdx, UShort eIdx,
553                   double time, int event, int pe, int ml, CmiObjId *id, 
554                   double recvT, double cpuT, int numPe)
555 {
556   new (&pool[numEntries++])
557     LogEntry(time, type, mIdx, eIdx, event, pe, ml, id, recvT, cpuT, numPe);
558   if(poolSize==numEntries) {
559     flushLogBuffer();
560 #if CMK_BLUEGENE_CHARM
561     extern int correctTimeLog;
562     if (correctTimeLog) CmiAbort("I/O interrupt!\n");
563 #endif
564   }
565 #if CMK_BLUEGENE_CHARM
566   switch (type) {
567     case BEGIN_PROCESSING:
568       pool[numEntries-1].recvTime = BgGetRecvTime();
569     case END_PROCESSING:
570     case BEGIN_COMPUTATION:
571     case END_COMPUTATION:
572     case CREATION:
573     case BEGIN_PACK:
574     case END_PACK:
575     case BEGIN_UNPACK:
576     case END_UNPACK:
577     case USER_EVENT_PAIR:
578       bgAddProjEvent(&pool[numEntries-1], numEntries-1, time, updateProjLog, &fp, BG_EVENT_PROJ);
579   }
580 #endif
581 }
582
583 void LogPool::add(UChar type,double time,UShort funcID,int lineNum,char *fileName){
584 #ifndef CMK_BLUEGENE_CHARM
585   new (&pool[numEntries++])
586         LogEntry(time,type,funcID,lineNum,fileName);
587   if(poolSize == numEntries){
588     flushLogBuffer();
589   }
590 #endif  
591 }
592
593
594   
595 void LogPool::addMemoryUsage(unsigned char type,double time,double memUsage){
596 #ifndef CMK_BLUEGENE_CHARM
597   new (&pool[numEntries++])
598         LogEntry(type,time,memUsage);
599   if(poolSize == numEntries){
600     flushLogBuffer();
601   }
602 #endif  
603         
604 }  
605
606
607
608 void LogPool::addUserSupplied(int data){
609         // add an event
610         add(USER_SUPPLIED, 0, 0, TraceTimer(), -1, -1, 0, 0, 0, 0, 0 );
611
612         // set the user supplied value for the previously created event 
613         pool[numEntries-1].setUserSuppliedData(data);
614   }
615
616
617 void LogPool::addUserSuppliedNote(char *note){
618         // add an event
619         add(USER_SUPPLIED_NOTE, 0, 0, TraceTimer(), -1, -1, 0, 0, 0, 0, 0 );
620
621         // set the user supplied note for the previously created event 
622         pool[numEntries-1].setUserSuppliedNote(note);
623   }
624
625 void LogPool::addUserSuppliedBracketedNote(char *note, int eventID, double bt, double et){
626   //CkPrintf("LogPool::addUserSuppliedBracketedNote eventID=%d\n", eventID);
627 #ifndef CMK_BLUEGENE_CHARM
628 #if CMK_SMP_TRACE_COMMTHREAD && MPI_SMP_TRACE_COMMTHREAD_HACK   
629 //This part of code is used  to combine the contiguous
630 //MPI_Test and MPI_Iprobe events to reduce the number of
631 //entries
632 #define MPI_TEST_EVENT_ID 60
633 #define MPI_IPROBE_EVENT_ID 70  
634 int lastEvent = pool[numEntries-1].event;
635 if((eventID==MPI_TEST_EVENT_ID || eventID==MPI_IPROBE_EVENT_ID) && (eventID==lastEvent)){
636     //just replace the endtime of last event
637     //CkPrintf("addUserSuppliedBracketNote: for event %d\n", lastEvent);
638     pool[numEntries].endTime = et;
639 }else{
640   new (&pool[numEntries++])
641         LogEntry(bt, et, USER_SUPPLIED_BRACKETED_NOTE, note, eventID);
642 }
643 #else
644   new (&pool[numEntries++])
645         LogEntry(bt, et, USER_SUPPLIED_BRACKETED_NOTE, note, eventID);
646 #endif
647   if(poolSize == numEntries){
648     flushLogBuffer();
649   }
650 #endif  
651 }
652
653
654 /* **CW** Not sure if this is the right thing to do. Feels more like
655    a hack than a solution to Sameer's request to add the destination
656    processor information to multicasts and broadcasts.
657
658    In the unlikely event this method is used for Broadcasts as well,
659    pelist == NULL will be used to indicate a global broadcast with 
660    num PEs.
661 */
662 void LogPool::addCreationMulticast(UShort mIdx, UShort eIdx, double time,
663                                    int event, int pe, int ml, CmiObjId *id,
664                                    double recvT, int numPe, int *pelist)
665 {
666   new (&pool[numEntries++])
667     LogEntry(time, mIdx, eIdx, event, pe, ml, id, recvT, numPe, pelist);
668   if(poolSize==numEntries) {
669     flushLogBuffer();
670   }
671 }
672
673 void LogPool::postProcessLog()
674 {
675 #if CMK_BLUEGENE_CHARM
676   bgUpdateProj(1);   // event type
677 #endif
678 }
679
680 // /** Constructor for a multicast log entry */
681 // 
682 //  THIS WAS MOVED TO trace-projections.h with the other constructors
683 // 
684 // LogEntry::LogEntry(double tm, unsigned short m, unsigned short e, int ev, int p,
685 //           int ml, CmiObjId *d, double rt, int numPe, int *pelist) 
686 // {
687 //     type = CREATION_MULTICAST; mIdx = m; eIdx = e; event = ev; pe = p; time = tm; msglen = ml;
688 //     if (d) id = *d; else {id.id[0]=id.id[1]=id.id[2]=id.id[3]=-1; };
689 //     recvTime = rt; 
690 //     numpes = numPe;
691 //     userSuppliedNote = NULL;
692 //     if (pelist != NULL) {
693 //      pes = new int[numPe];
694 //      for (int i=0; i<numPe; i++) {
695 //        pes[i] = pelist[i];
696 //      }
697 //     } else {
698 //      pes= NULL;
699 //     }
700 // }
701
702 void LogEntry::addPapi(int numPapiEvts, int *papi_ids, LONG_LONG_PAPI *papiVals)
703 {
704 #if CMK_HAS_COUNTER_PAPI
705   numPapiEvents = numPapiEvts;
706   if (papiVals != NULL) {
707     papiIDs = new int[numPapiEvents];
708     papiValues = new LONG_LONG_PAPI[numPapiEvents];
709     for (int i=0; i<numPapiEvents; i++) {
710       papiIDs[i] = papi_ids[i];
711       papiValues[i] = papiVals[i];
712     }
713   }
714 #endif
715 }
716
717
718
719 void LogEntry::pup(PUP::er &p)
720 {
721   int i;
722   CMK_TYPEDEF_UINT8 itime, iEndTime, irecvtime, icputime;
723   char ret = '\n';
724
725   p|type;
726   if (p.isPacking()) itime = (CMK_TYPEDEF_UINT8)(1.0e6*time);
727   if (p.isPacking()) iEndTime = (CMK_TYPEDEF_UINT8)(1.0e6*endTime);
728
729   switch (type) {
730     case USER_EVENT:
731     case USER_EVENT_PAIR:
732       p|mIdx; p|itime; p|event; p|pe;
733       break;
734     case BEGIN_IDLE:
735     case END_IDLE:
736     case BEGIN_PACK:
737     case END_PACK:
738     case BEGIN_UNPACK:
739     case END_UNPACK:
740       p|itime; p|pe; 
741       break;
742     case BEGIN_PROCESSING:
743       if (p.isPacking()) {
744         irecvtime = (CMK_TYPEDEF_UINT8)(recvTime==-1?-1:1.0e6*recvTime);
745         icputime = (CMK_TYPEDEF_UINT8)(1.0e6*cputime);
746       }
747       p|mIdx; p|eIdx; p|itime; p|event; p|pe; 
748       p|msglen; p|irecvtime; 
749       p|id.id[0]; p|id.id[1]; p|id.id[2]; p|id.id[3];
750       p|icputime;
751 #if CMK_HAS_COUNTER_PAPI
752       p|numPapiEvents;
753       for (i=0; i<numPapiEvents; i++) {
754         // not yet!!!
755         //      p|papiIDs[i]; 
756         p|papiValues[i];
757         
758       }
759 #else
760       p|numPapiEvents;     // non papi version has value 0
761 #endif
762       if (p.isUnpacking()) {
763         recvTime = irecvtime/1.0e6;
764         cputime = icputime/1.0e6;
765       }
766       break;
767     case END_PROCESSING:
768       if (p.isPacking()) icputime = (CMK_TYPEDEF_UINT8)(1.0e6*cputime);
769       p|mIdx; p|eIdx; p|itime; p|event; p|pe; p|msglen; p|icputime;
770 #if CMK_HAS_COUNTER_PAPI
771       p|numPapiEvents;
772       for (i=0; i<numPapiEvents; i++) {
773         // not yet!!!
774         //      p|papiIDs[i];
775         p|papiValues[i];
776       }
777 #else
778       p|numPapiEvents;  // non papi version has value 0
779 #endif
780       if (p.isUnpacking()) cputime = icputime/1.0e6;
781       break;
782     case USER_SUPPLIED:
783           p|userSuppliedData;
784           p|itime;
785         break;
786     case USER_SUPPLIED_NOTE:
787           p|itime;
788           int length;
789           if (p.isPacking()) length = strlen(userSuppliedNote);
790           p | length;
791           char space;
792           space = ' ';
793           p | space;
794           if (p.isUnpacking()) {
795             userSuppliedNote = new char[length+1];
796             userSuppliedNote[length] = '\0';
797           }
798           PUParray(p,userSuppliedNote, length);
799           break;
800     case USER_SUPPLIED_BRACKETED_NOTE:
801       //CkPrintf("Writting out a USER_SUPPLIED_BRACKETED_NOTE\n");
802           p|itime;
803           p|iEndTime;
804           p|event;
805           int length2;
806           if (p.isPacking()) length2 = strlen(userSuppliedNote);
807           p | length2;
808           char space2;
809           space2 = ' ';
810           p | space2;
811           if (p.isUnpacking()) {
812             userSuppliedNote = new char[length+1];
813             userSuppliedNote[length] = '\0';
814           }
815           PUParray(p,userSuppliedNote, length2);
816           break;
817     case MEMORY_USAGE_CURRENT:
818       p | memUsage;
819       p | itime;
820         break;
821     case CREATION:
822       if (p.isPacking()) irecvtime = (CMK_TYPEDEF_UINT8)(1.0e6*recvTime);
823       p|mIdx; p|eIdx; p|itime;
824       p|event; p|pe; p|msglen; p|irecvtime;
825       if (p.isUnpacking()) recvTime = irecvtime/1.0e6;
826       break;
827     case CREATION_BCAST:
828       if (p.isPacking()) irecvtime = (CMK_TYPEDEF_UINT8)(1.0e6*recvTime);
829       p|mIdx; p|eIdx; p|itime;
830       p|event; p|pe; p|msglen; p|irecvtime; p|numpes;
831       if (p.isUnpacking()) recvTime = irecvtime/1.0e6;
832       break;
833     case CREATION_MULTICAST:
834       if (p.isPacking()) irecvtime = (CMK_TYPEDEF_UINT8)(1.0e6*recvTime);
835       p|mIdx; p|eIdx; p|itime;
836       p|event; p|pe; p|msglen; p|irecvtime; p|numpes;
837       if (p.isUnpacking()) pes = numpes?new int[numpes]:NULL;
838       for (i=0; i<numpes; i++) p|pes[i];
839       if (p.isUnpacking()) recvTime = irecvtime/1.0e6;
840       break;
841     case MESSAGE_RECV:
842       p|mIdx; p|eIdx; p|itime; p|event; p|pe; p|msglen;
843       break;
844
845     case ENQUEUE:
846     case DEQUEUE:
847       p|mIdx; p|itime; p|event; p|pe;
848       break;
849
850     case BEGIN_INTERRUPT:
851     case END_INTERRUPT:
852       p|itime; p|event; p|pe;
853       break;
854
855       // **CW** absolute timestamps are used here to support a quick
856       // way of determining the total time of a run in projections
857       // visualization.
858     case BEGIN_COMPUTATION:
859     case END_COMPUTATION:
860     case BEGIN_TRACE:
861     case END_TRACE:
862       p|itime;
863       break;
864     case BEGIN_FUNC:
865         p | itime;
866         p | mIdx;
867         p | event;
868         if(!p.isUnpacking()){
869                 p(fName,flen-1);
870         }
871         break;
872     case END_FUNC:
873         p | itime;
874         p | mIdx;
875         break;
876     default:
877       CmiError("***Internal Error*** Wierd Event %d.\n", type);
878       break;
879   }
880   if (p.isUnpacking()) time = itime/1.0e6;
881   p|ret;
882 }
883
884 TraceProjections::TraceProjections(char **argv): 
885   curevent(0), inEntry(0), computationStarted(0), 
886   converseExit(0), endTime(0.0), traceNestedEvents(0)
887 {
888   //  CkPrintf("Trace projections dummy constructor called on %d\n",CkMyPe());
889
890   if (CkpvAccess(traceOnPe) == 0) return;
891
892   CtvInitialize(int,curThreadEvent);
893   CkpvInitialize(int, CtrLogBufSize);
894   CkpvInitialize(bool, useOutlierAnalysis);
895   CkpvAccess(CtrLogBufSize) = DefaultLogBufSize;
896   CtvAccess(curThreadEvent)=0;
897   CkpvAccess(useOutlierAnalysis) =
898     CmiGetArgFlagDesc(argv, "+outlier", "Perform Outlier Analysis");
899   if (CmiGetArgIntDesc(argv,"+logsize",&CkpvAccess(CtrLogBufSize), 
900                        "Log entries to buffer per I/O")) {
901     if (CkMyPe() == 0) {
902       CmiPrintf("Trace: logsize: %d\n", CkpvAccess(CtrLogBufSize));
903     }
904   }
905   checknested = 
906     CmiGetArgFlagDesc(argv,"+checknested",
907                       "check projections nest begin end execute events");
908   traceNestedEvents = 
909     CmiGetArgFlagDesc(argv,"+tracenested",
910               "trace projections nest begin/end execute events");
911   int binary = 
912     CmiGetArgFlagDesc(argv,"+binary-trace",
913                       "Write log files in binary format");
914 #if CMK_PROJECTIONS_USE_ZLIB
915   int compressed = CmiGetArgFlagDesc(argv,"+gz-trace","Write log files pre-compressed with gzip");
916 #else
917   // consume the flag so there's no confusing
918   CmiGetArgFlagDesc(argv,"+gz-trace",
919                     "Write log files pre-compressed with gzip");
920   if(CkMyPe() == 0) CkPrintf("Warning> gz-trace is not supported on this machine!\n");
921 #endif
922
923   // **CW** default to non delta log encoding. The user may choose to do
924   // create both logs (for debugging) or just the old log timestamping
925   // (for compatibility).
926   // Generating just the non delta log takes precedence over generating
927   // both logs (if both arguments appear on the command line).
928
929   // switch to OLD log format until everything works // Gengbin
930   nonDeltaLog = 1;
931   deltaLog = 0;
932   deltaLog = CmiGetArgFlagDesc(argv, "+logDelta",
933                                   "Generate Delta encoded and simple timestamped log files");
934
935   _logPool = new LogPool(CkpvAccess(traceRoot));
936   _logPool->setBinary(binary);
937 #if CMK_PROJECTIONS_USE_ZLIB
938   _logPool->setCompressed(compressed);
939 #endif
940   if (CkMyPe() == 0) {
941     _logPool->createSts();
942     _logPool->createRC();
943   }
944   funcCount=1;
945
946 #if CMK_HAS_COUNTER_PAPI
947   // We initialize and create the event sets for use with PAPI here.
948   int papiRetValue = PAPI_library_init(PAPI_VER_CURRENT);
949   if (papiRetValue != PAPI_VER_CURRENT) {
950     CmiAbort("PAPI Library initialization failure!\n");
951   }
952   // PAPI 3 mandates the initialization of the set to PAPI_NULL
953   papiEventSet = PAPI_NULL; 
954   if (PAPI_create_eventset(&papiEventSet) != PAPI_OK) {
955     CmiAbort("PAPI failed to create event set!\n");
956   }
957   papiRetValue = PAPI_add_events(papiEventSet, papiEvents, numPAPIEvents);
958   if (papiRetValue != PAPI_OK) {
959     if (papiRetValue == PAPI_ECNFLCT) {
960       CmiAbort("PAPI events conflict! Please re-assign event types!\n");
961     } else {
962       CmiAbort("PAPI failed to add designated events!\n");
963     }
964   }
965   papiValues = new long_long[numPAPIEvents];
966   memset(papiValues, 0, numPAPIEvents*sizeof(long_long));
967 #endif
968 }
969
970 int TraceProjections::traceRegisterUserEvent(const char* evt, int e)
971 {
972   OPTIMIZED_VERSION
973   CkAssert(e==-1 || e>=0);
974   CkAssert(evt != NULL);
975   int event;
976   int biggest = -1;
977   for (int i=0; i<CkpvAccess(usrEvents)->length(); i++) {
978     int cur = (*CkpvAccess(usrEvents))[i]->e;
979     if (cur == e) {
980       //CmiPrintf("%s %s\n", (*CkpvAccess(usrEvents))[i]->str, evt);
981       if (strcmp((*CkpvAccess(usrEvents))[i]->str, evt) == 0) 
982         return e;
983       else
984         CmiAbort("UserEvent double registered!");
985     }
986     if (cur > biggest) biggest = cur;
987   }
988   // if no user events have so far been registered. biggest will be -1
989   // and hence newly assigned event numbers will begin from 0.
990   if (e==-1) event = biggest+1;  // automatically assign new event number
991   else event = e;
992   CkpvAccess(usrEvents)->push_back(new UsrEvent(event,(char *)evt));
993   return event;
994 }
995
996 void TraceProjections::traceClearEps(void)
997 {
998   // In trace-summary, this zeros out the EP bins, to eliminate noise
999   // from startup.  Here, this isn't useful, since we can do that in
1000   // post-processing
1001 }
1002
1003 void TraceProjections::traceWriteSts(void)
1004 {
1005   if(CkMyPe()==0)
1006     _logPool->writeSts(this);
1007 }
1008
1009 // This is called when Converse closes during ConverseCommonExit()
1010 // 
1011 // Some programs bypass 
1012 // CkExit (like NAMD, which eventually calls ConverseExit), modules
1013 // like traces will have to pretend to shutdown as if CkExit was called
1014 // but at the same time avoid making subsequent CkExit calls (which is
1015 // usually required for allowing other modules to shutdown).
1016 //
1017 // Note that we can only get here if CkExit was not called, since the
1018 // trace module will un-register itself from TraceArray if it did.
1019 void TraceProjections::traceClose(void)
1020 {
1021 #ifdef PROJ_ANALYSIS
1022   // CkPrintf("CkExit was not called on shutdown on [%d]\n", CkMyPe());
1023   // sets the flag that tells the code not to make the CkExit call later
1024   converseExit = 1;
1025   if (CkMyPe() == 0) {
1026     CProxy_TraceProjectionsBOC bocProxy(traceProjectionsGID);
1027     bocProxy.shutdownAnalysis();
1028   }
1029 #else
1030   // we've already deleted the logpool, so multiple calls to traceClose
1031   // are tolerated.
1032   if (_logPool == NULL) {
1033     return;
1034   }
1035   if(CkMyPe()==0){
1036     _logPool->writeSts(this);
1037   }
1038   CkpvAccess(_trace)->endComputation();
1039   delete _logPool;              // will write
1040   // remove myself from traceArray so that no tracing will be called.
1041   CkpvAccess(_traces)->removeTrace(this);
1042 #endif
1043 }
1044
1045 // This is meant to be called internally rather than by converse.
1046 void TraceProjections::closeTrace() {
1047   //  CkPrintf("Close Trace called on [%d]\n", CkMyPe());
1048   if (CkMyPe() == 0) {
1049     // CkPrintf("Pe 0 will now write sts and projrc files\n");
1050     _logPool->writeSts(this);
1051     _logPool->writeRC();
1052     // CkPrintf("Pe 0 has now written sts and projrc files\n");
1053   }
1054   delete _logPool;       // will write logs to file
1055 }
1056
1057 void TraceProjections::traceBegin(void)
1058 {
1059   if (!computationStarted) return;
1060   _logPool->add(BEGIN_TRACE, 0, 0, TraceTimer(), curevent++, CkMyPe());
1061 }
1062
1063 void TraceProjections::traceEnd(void)
1064 {
1065   _logPool->add(END_TRACE, 0, 0, TraceTimer(), curevent++, CkMyPe());
1066 }
1067
1068 #if CMK_SMP_TRACE_COMMTHREAD
1069 void TraceProjections::traceBeginOnCommThread()
1070 {
1071   if (!computationStarted) return;
1072   _logPool->add(BEGIN_TRACE, 0, 0, TraceTimer(), curevent++, CmiNumPes()+CmiMyNode());
1073 }
1074
1075 void TraceProjections::traceEndOnCommThread()
1076 {
1077   _logPool->add(END_TRACE, 0, 0, TraceTimer(), curevent++, CmiNumPes()+CmiMyNode());
1078 }
1079 #endif
1080
1081 void TraceProjections::userEvent(int e)
1082 {
1083   if (!computationStarted) return;
1084   _logPool->add(USER_EVENT, e, 0, TraceTimer(),curevent++,CkMyPe());
1085 }
1086
1087 void TraceProjections::userBracketEvent(int e, double bt, double et)
1088 {
1089   if (!computationStarted) return;
1090   // two events record Begin/End of event e.
1091   _logPool->add(USER_EVENT_PAIR, e, 0, TraceTimer(bt), curevent, CkMyPe());
1092   _logPool->add(USER_EVENT_PAIR, e, 0, TraceTimer(et), curevent++, CkMyPe());
1093 }
1094
1095 void TraceProjections::userSuppliedData(int d)
1096 {
1097   if (!computationStarted) return;
1098   _logPool->addUserSupplied(d);
1099 }
1100
1101 void TraceProjections::userSuppliedNote(char *note)
1102 {
1103   if (!computationStarted) return;
1104   _logPool->addUserSuppliedNote(note);
1105 }
1106
1107
1108 void TraceProjections::userSuppliedBracketedNote(char *note, int eventID, double bt, double et)
1109 {
1110   if (!computationStarted) return;
1111   _logPool->addUserSuppliedBracketedNote(note,  eventID,  bt, et);
1112 }
1113
1114 void TraceProjections::memoryUsage(double m)
1115 {
1116   if (!computationStarted) return;
1117   _logPool->addMemoryUsage(MEMORY_USAGE_CURRENT, TraceTimer(), m );
1118   
1119 }
1120
1121
1122 void TraceProjections::creation(envelope *e, int ep, int num)
1123 {
1124   double curTime = TraceTimer();
1125   if (e == 0) {
1126     CtvAccess(curThreadEvent) = curevent;
1127     _logPool->add(CREATION, ForChareMsg, ep, curTime,
1128                   curevent++, CkMyPe(), 0, NULL, 0, 0.0);
1129   } else {
1130     int type=e->getMsgtype();
1131     e->setEvent(curevent);
1132     if (num > 1) {
1133       _logPool->add(CREATION_BCAST, type, ep, curTime,
1134                     curevent++, CkMyPe(), e->getTotalsize(), 
1135                     NULL, 0, 0.0, num);
1136     } else {
1137       _logPool->add(CREATION, type, ep, curTime,
1138                     curevent++, CkMyPe(), e->getTotalsize(), 
1139                     NULL, 0, 0.0);
1140     }
1141   }
1142 }
1143
1144 /* **CW** Non-disruptive attempt to add destination PE knowledge to
1145    Communication Library-specific Multicasts via new event 
1146    CREATION_MULTICAST.
1147 */
1148
1149 void TraceProjections::creationMulticast(envelope *e, int ep, int num,
1150                                          int *pelist)
1151 {
1152   double curTime = TraceTimer();
1153   if (e==0) {
1154     CtvAccess(curThreadEvent)=curevent;
1155     _logPool->addCreationMulticast(ForChareMsg, ep, curTime, curevent++,
1156                                    CkMyPe(), 0, 0, 0.0, num, pelist);
1157   } else {
1158     int type=e->getMsgtype();
1159     e->setEvent(curevent);
1160     _logPool->addCreationMulticast(type, ep, curTime, curevent++, CkMyPe(),
1161                                    e->getTotalsize(), 0, 0.0, num, pelist);
1162   }
1163 }
1164
1165 void TraceProjections::creationDone(int num)
1166 {
1167   // modified the creation done time of the last num log entries
1168   // FIXME: totally a hack
1169   double curTime = TraceTimer();
1170   int idx = _logPool->numEntries-1;
1171   while (idx >=0 && num >0 ) {
1172     LogEntry &log = _logPool->pool[idx];
1173     if ((log.type == CREATION) ||
1174         (log.type == CREATION_BCAST) ||
1175         (log.type == CREATION_MULTICAST)) {
1176       log.recvTime = curTime - log.time;
1177       num --;
1178     }
1179     idx--;
1180   }
1181 }
1182
1183 void TraceProjections::beginExecute(CmiObjId *tid)
1184 {
1185 #if CMK_HAS_COUNTER_PAPI
1186   if (PAPI_read(papiEventSet, papiValues) != PAPI_OK) {
1187     CmiAbort("PAPI failed to read at begin execute!\n");
1188   }
1189 #endif
1190   if (checknested && inEntry) CmiAbort("Nested Begin Execute!\n");
1191   execEvent = CtvAccess(curThreadEvent);
1192   execEp = (-1);
1193   _logPool->add(BEGIN_PROCESSING,ForChareMsg,_threadEP,TraceTimer(),
1194                 execEvent,CkMyPe(), 0, tid);
1195 #if CMK_HAS_COUNTER_PAPI
1196   _logPool->addPapi(numPAPIEvents, papiEvents, papiValues);
1197 #endif
1198   inEntry = 1;
1199 }
1200
1201 void TraceProjections::beginExecute(envelope *e)
1202 {
1203   if(e==0) {
1204 #if CMK_HAS_COUNTER_PAPI
1205     if (PAPI_read(papiEventSet, papiValues) != PAPI_OK) {
1206       CmiAbort("PAPI failed to read at begin execute!\n");
1207     }
1208 #endif
1209     if (checknested && inEntry) CmiAbort("Nested Begin Execute!\n");
1210     execEvent = CtvAccess(curThreadEvent);
1211     execEp = (-1);
1212     _logPool->add(BEGIN_PROCESSING,ForChareMsg,_threadEP,TraceTimer(),
1213                   execEvent,CkMyPe(), 0, NULL, 0.0, TraceCpuTimer());
1214 #if CMK_HAS_COUNTER_PAPI
1215     _logPool->addPapi(numPAPIEvents, papiEvents, papiValues);
1216 #endif
1217     inEntry = 1;
1218   } else {
1219     beginExecute(e->getEvent(),e->getMsgtype(),e->getEpIdx(),
1220                  e->getSrcPe(),e->getTotalsize());
1221   }
1222 }
1223
1224 void TraceProjections::beginExecute(int event, int msgType, int ep, int srcPe,
1225                                     int mlen, CmiObjId *idx)
1226 {
1227   if (traceNestedEvents) {
1228     if (! nestedEvents.isEmpty()) {
1229       endExecuteLocal();
1230     }
1231     nestedEvents.enq(NestedEvent(event, msgType, ep, srcPe, mlen, idx));
1232   }
1233   beginExecuteLocal(event, msgType, ep, srcPe, mlen, idx);
1234 }
1235
1236 void TraceProjections::beginExecuteLocal(int event, int msgType, int ep, int srcPe,
1237                                     int mlen, CmiObjId *idx)
1238 {
1239 #if CMK_HAS_COUNTER_PAPI
1240   if (PAPI_read(papiEventSet, papiValues) != PAPI_OK) {
1241     CmiAbort("PAPI failed to read at begin execute!\n");
1242   }
1243 #endif
1244   if (checknested && inEntry) CmiAbort("Nested Begin Execute!\n");
1245   execEvent=event;
1246   execEp=ep;
1247   execPe=srcPe;
1248   _logPool->add(BEGIN_PROCESSING,msgType,ep,TraceTimer(),event,
1249                 srcPe, mlen, idx, 0.0, TraceCpuTimer());
1250 #if CMK_HAS_COUNTER_PAPI
1251   _logPool->addPapi(numPAPIEvents, papiEvents, papiValues);
1252 #endif
1253   inEntry = 1;
1254 }
1255
1256 void TraceProjections::endExecute(void)
1257 {
1258   if (traceNestedEvents) nestedEvents.deq();
1259   endExecuteLocal();
1260   if (traceNestedEvents) {
1261     if (! nestedEvents.isEmpty()) {
1262       NestedEvent &ne = nestedEvents.peek();
1263       beginExecuteLocal(ne.event, ne.msgType, ne.ep, ne.srcPe, ne.ml, ne.idx);
1264     }
1265   }
1266 }
1267
1268 void TraceProjections::endExecuteLocal(void)
1269 {
1270 #if CMK_HAS_COUNTER_PAPI
1271   if (PAPI_read(papiEventSet, papiValues) != PAPI_OK) {
1272     CmiAbort("PAPI failed to read at end execute!\n");
1273   }
1274 #endif
1275   if (checknested && !inEntry) CmiAbort("Nested EndExecute!\n");
1276   double cputime = TraceCpuTimer();
1277   if(execEp == (-1)) {
1278     _logPool->add(END_PROCESSING, 0, _threadEP, TraceTimer(),
1279                   execEvent, CkMyPe(), 0, NULL, 0.0, cputime);
1280   } else {
1281     _logPool->add(END_PROCESSING, 0, execEp, TraceTimer(),
1282                   execEvent, execPe, 0, NULL, 0.0, cputime);
1283   }
1284 #if CMK_HAS_COUNTER_PAPI
1285   _logPool->addPapi(numPAPIEvents, papiEvents, papiValues);
1286 #endif
1287   inEntry = 0;
1288 }
1289
1290 void TraceProjections::messageRecv(char *env, int pe)
1291 {
1292 #if 0
1293   envelope *e = (envelope *)env;
1294   int msgType = e->getMsgtype();
1295   int ep = e->getEpIdx();
1296 #if 0
1297   if (msgType==NewChareMsg || msgType==NewVChareMsg
1298           || msgType==ForChareMsg || msgType==ForVidMsg
1299           || msgType==BocInitMsg || msgType==NodeBocInitMsg
1300           || msgType==ForBocMsg || msgType==ForNodeBocMsg)
1301     ep = e->getEpIdx();
1302   else
1303     ep = _threadEP;
1304 #endif
1305   _logPool->add(MESSAGE_RECV, msgType, ep, TraceTimer(),
1306                 curevent++, e->getSrcPe(), e->getTotalsize());
1307 #endif
1308 }
1309
1310 void TraceProjections::beginIdle(double curWallTime)
1311 {
1312   _logPool->add(BEGIN_IDLE, 0, 0, TraceTimer(curWallTime), 0, CkMyPe());
1313 }
1314
1315 void TraceProjections::endIdle(double curWallTime)
1316 {
1317   _logPool->add(END_IDLE, 0, 0, TraceTimer(curWallTime), 0, CkMyPe());
1318 }
1319
1320 void TraceProjections::beginPack(void)
1321 {
1322   _logPool->add(BEGIN_PACK, 0, 0, TraceTimer(), 0, CkMyPe());
1323 }
1324
1325 void TraceProjections::endPack(void)
1326 {
1327   _logPool->add(END_PACK, 0, 0, TraceTimer(), 0, CkMyPe());
1328 }
1329
1330 void TraceProjections::beginUnpack(void)
1331 {
1332   _logPool->add(BEGIN_UNPACK, 0, 0, TraceTimer(), 0, CkMyPe());
1333 }
1334
1335 void TraceProjections::endUnpack(void)
1336 {
1337   _logPool->add(END_UNPACK, 0, 0, TraceTimer(), 0, CkMyPe());
1338 }
1339
1340 void TraceProjections::enqueue(envelope *) {}
1341
1342 void TraceProjections::dequeue(envelope *) {}
1343
1344 void TraceProjections::beginComputation(void)
1345 {
1346   computationStarted = 1;
1347
1348   // Executes the callback function provided by the machine
1349   // layer. This is the proper method to register user events in a
1350   // machine layer because projections is a charm++ module.
1351   if (CkpvAccess(traceOnPe) != 0) {
1352     void (*ptr)() = registerMachineUserEvents();
1353     if (ptr != NULL) {
1354       ptr();
1355     }
1356   }
1357 //  CkpvAccess(traceInitTime) = TRACE_TIMER();
1358 //  CkpvAccess(traceInitCpuTime) = TRACE_CPUTIMER();
1359   _logPool->add(BEGIN_COMPUTATION, 0, 0, TraceTimer(), -1, -1);
1360 #if CMK_HAS_COUNTER_PAPI
1361   // we start the counters here
1362   if (PAPI_start(papiEventSet) != PAPI_OK) {
1363     CmiAbort("PAPI failed to start designated counters!\n");
1364   }
1365 #endif
1366 }
1367
1368 void TraceProjections::endComputation(void)
1369 {
1370 #if CMK_HAS_COUNTER_PAPI
1371   // we stop the counters here. A silent failure is alright since we
1372   // are already at the end of the program.
1373   if (PAPI_stop(papiEventSet, papiValues) != PAPI_OK) {
1374     CkPrintf("Warning: PAPI failed to stop correctly!\n");
1375   }
1376   // NOTE: We should not do a complete close of PAPI until after the
1377   // sts writer is done.
1378 #endif
1379   endTime = TraceTimer();
1380   _logPool->add(END_COMPUTATION, 0, 0, endTime, -1, -1);
1381   /*
1382   CkPrintf("End Computation [%d] records time as %lf\n", CkMyPe(), 
1383            endTime*1e06);
1384   */
1385 }
1386
1387 int TraceProjections::idxRegistered(int idx)
1388 {
1389     int idxVecLen = idxVec.size();
1390     for(int i=0; i<idxVecLen; i++)
1391     {
1392         if(idx == idxVec[i])
1393             return 1;
1394     }
1395     return 0;
1396 }
1397
1398 void TraceProjections::regFunc(const char *name, int &idx, int idxSpecifiedByUser){
1399     StrKey k((char*)name,strlen(name));
1400     int num = funcHashtable.get(k);
1401     
1402     if(num!=0) {
1403         return;
1404         //as for mpi programs, the same function may be registered for several times
1405         //CmiError("\"%s has been already registered! Please change the name!\"\n", name);
1406     }
1407     
1408     int isIdxExisting=0;
1409     if(idxSpecifiedByUser)
1410         isIdxExisting=idxRegistered(idx);
1411     if(isIdxExisting){
1412         return;
1413         //same reason with num!=0
1414         //CmiError("The identifier %d for the trace function has been already registered!", idx);
1415     }
1416
1417     if(idxSpecifiedByUser) {
1418         char *st = new char[strlen(name)+1];
1419         memcpy(st,name,strlen(name)+1);
1420         StrKey *newKey = new StrKey(st,strlen(st));
1421         int &ref = funcHashtable.put(*newKey);
1422         ref=idx;
1423         funcCount++;
1424         idxVec.push_back(idx);  
1425     } else {
1426         char *st = new char[strlen(name)+1];
1427         memcpy(st,name,strlen(name)+1);
1428         StrKey *newKey = new StrKey(st,strlen(st));
1429         int &ref = funcHashtable.put(*newKey);
1430         ref=funcCount;
1431         num = funcCount;
1432         funcCount++;
1433         idx = num;
1434         idxVec.push_back(idx);
1435     }
1436 }
1437
1438 void TraceProjections::beginFunc(char *name,char *file,int line){
1439         StrKey k(name,strlen(name));    
1440         unsigned short  num = (unsigned short)funcHashtable.get(k);
1441         beginFunc(num,file,line);
1442 }
1443
1444 void TraceProjections::beginFunc(int idx,char *file,int line){
1445         if(idx <= 0){
1446                 CmiError("Unregistered function id %d being used in %s:%d \n",idx,file,line);
1447         }       
1448         _logPool->add(BEGIN_FUNC,TraceTimer(),idx,line,file);
1449 }
1450
1451 void TraceProjections::endFunc(char *name){
1452         StrKey k(name,strlen(name));    
1453         int num = funcHashtable.get(k);
1454         endFunc(num);   
1455 }
1456
1457 void TraceProjections::endFunc(int num){
1458         if(num <= 0){
1459                 printf("endFunc without start :O\n");
1460         }
1461         _logPool->add(END_FUNC,TraceTimer(),num,0,NULL);
1462 }
1463
1464 // specialized PUP:ers for handling trace projections logs
1465 void toProjectionsFile::bytes(void *p,int n,size_t itemSize,dataType t)
1466 {
1467   for (int i=0;i<n;i++) 
1468     switch(t) {
1469     case Tchar: CheckAndFPrintF(f,"%c",((char *)p)[i]); break;
1470     case Tuchar:
1471     case Tbyte: CheckAndFPrintF(f,"%d",((unsigned char *)p)[i]); break;
1472     case Tshort: CheckAndFPrintF(f," %d",((short *)p)[i]); break;
1473     case Tushort: CheckAndFPrintF(f," %u",((unsigned short *)p)[i]); break;
1474     case Tint: CheckAndFPrintF(f," %d",((int *)p)[i]); break;
1475     case Tuint: CheckAndFPrintF(f," %u",((unsigned int *)p)[i]); break;
1476     case Tlong: CheckAndFPrintF(f," %ld",((long *)p)[i]); break;
1477     case Tulong: CheckAndFPrintF(f," %lu",((unsigned long *)p)[i]); break;
1478     case Tfloat: CheckAndFPrintF(f," %.7g",((float *)p)[i]); break;
1479     case Tdouble: CheckAndFPrintF(f," %.15g",((double *)p)[i]); break;
1480 #ifdef CMK_PUP_LONG_LONG
1481     case Tlonglong: CheckAndFPrintF(f," %lld",((CMK_TYPEDEF_INT8 *)p)[i]); break;
1482     case Tulonglong: CheckAndFPrintF(f," %llu",((CMK_TYPEDEF_UINT8 *)p)[i]); break;
1483 #endif
1484     default: CmiAbort("Unrecognized pup type code!");
1485     };
1486 }
1487
1488 void fromProjectionsFile::bytes(void *p,int n,size_t itemSize,dataType t)
1489 {
1490   for (int i=0;i<n;i++) 
1491     switch(t) {
1492     case Tchar: { 
1493       char c = fgetc(f);
1494       if (c==EOF)
1495         parseError("Could not match character");
1496       else
1497         ((char *)p)[i] = c;
1498       break;
1499     }
1500     case Tuchar:
1501     case Tbyte: ((unsigned char *)p)[i]=(unsigned char)readInt("%d"); break;
1502     case Tshort:((short *)p)[i]=(short)readInt(); break;
1503     case Tushort: ((unsigned short *)p)[i]=(unsigned short)readUint(); break;
1504     case Tint:  ((int *)p)[i]=readInt(); break;
1505     case Tuint: ((unsigned int *)p)[i]=readUint(); break;
1506     case Tlong: ((long *)p)[i]=readInt(); break;
1507     case Tulong:((unsigned long *)p)[i]=readUint(); break;
1508     case Tfloat: ((float *)p)[i]=(float)readDouble(); break;
1509     case Tdouble:((double *)p)[i]=readDouble(); break;
1510 #ifdef CMK_PUP_LONG_LONG
1511     case Tlonglong: ((CMK_TYPEDEF_INT8 *)p)[i]=readLongInt(); break;
1512     case Tulonglong: ((CMK_TYPEDEF_UINT8 *)p)[i]=readLongInt(); break;
1513 #endif
1514     default: CmiAbort("Unrecognized pup type code!");
1515     };
1516 }
1517
1518 #if CMK_PROJECTIONS_USE_ZLIB
1519 void toProjectionsGZFile::bytes(void *p,int n,size_t itemSize,dataType t)
1520 {
1521   for (int i=0;i<n;i++) 
1522     switch(t) {
1523     case Tchar: gzprintf(f,"%c",((char *)p)[i]); break;
1524     case Tuchar:
1525     case Tbyte: gzprintf(f,"%d",((unsigned char *)p)[i]); break;
1526     case Tshort: gzprintf(f," %d",((short *)p)[i]); break;
1527     case Tushort: gzprintf(f," %u",((unsigned short *)p)[i]); break;
1528     case Tint: gzprintf(f," %d",((int *)p)[i]); break;
1529     case Tuint: gzprintf(f," %u",((unsigned int *)p)[i]); break;
1530     case Tlong: gzprintf(f," %ld",((long *)p)[i]); break;
1531     case Tulong: gzprintf(f," %lu",((unsigned long *)p)[i]); break;
1532     case Tfloat: gzprintf(f," %.7g",((float *)p)[i]); break;
1533     case Tdouble: gzprintf(f," %.15g",((double *)p)[i]); break;
1534 #ifdef CMK_PUP_LONG_LONG
1535     case Tlonglong: gzprintf(f," %lld",((CMK_TYPEDEF_INT8 *)p)[i]); break;
1536     case Tulonglong: gzprintf(f," %llu",((CMK_TYPEDEF_UINT8 *)p)[i]); break;
1537 #endif
1538     default: CmiAbort("Unrecognized pup type code!");
1539     };
1540 }
1541 #endif
1542
1543 #ifdef PROJ_ANALYSIS
1544 void TraceProjectionsBOC::shutdownAnalysis() {
1545   if (CkMyPe() == 0) {
1546     analysisStartTime = CmiWallTimer();
1547   }
1548   //  CkPrintf("Shutdown analysis called on [%d]\n",CkMyPe());
1549   CkpvAccess(_trace)->endComputation();
1550   // no more tracing for projections on this processor after this point. 
1551   // Note that clear must be called after remove, or bad things will happen.
1552   CkpvAccess(_traces)->removeTrace(CkpvAccess(_trace));
1553   CkpvAccess(_traces)->clearTrace();
1554
1555   // From this point, we start a chain of reductions and broadcasts to
1556   // perform final online analysis activities, ending with endTimeReduction
1557   if (CkpvAccess(useOutlierAnalysis)) {
1558     thisProxy[CkMyPe()].startOutlierAnalysis();
1559   } else {
1560     thisProxy[CkMyPe()].startEndTimeAnalysis();
1561   }
1562 }
1563
1564 void TraceProjectionsBOC::startOutlierAnalysis() {
1565   // assumes the presence of the complete logs on this processor.
1566   LogPool *pool = CkpvAccess(_trace)->_logPool;
1567
1568   // this array stores entry method and idle times (hence +1) and 
1569   // is twice as long to store their squares for statistical analysis
1570   // when the reduction is performed.
1571   int numEvents = _entryTable.size()+1;
1572   execTimes = new double[numEvents*2];
1573   for (int i=0; i<numEvents*2; i++) {
1574     execTimes[i] = 0.0;
1575   }
1576
1577   bool markedBegin = false;
1578   bool markedIdle = false;
1579   double beginBlockTime = 0.0;
1580   double beginIdleBlockTime = 0.0;
1581
1582   for (int i=0; i<pool->numEntries; i++) {
1583     if (pool->pool[i].type == BEGIN_PROCESSING) {
1584       // check pairing
1585       if (!markedBegin) {
1586         markedBegin = true;
1587       }
1588       beginBlockTime = pool->pool[i].time;
1589     } else if (pool->pool[i].type == END_PROCESSING) {
1590       // check pairing
1591       // if End without a begin, just ignore
1592       // this event.
1593       if (markedBegin) {
1594         markedBegin = false;
1595         if (pool->pool[i].event < 0)
1596         {
1597           // ignore dummy events
1598           break;
1599         }
1600         execTimes[pool->pool[i].eIdx] +=
1601           pool->pool[i].time - beginBlockTime;
1602       }
1603     } else if (pool->pool[i].type == BEGIN_IDLE) {
1604       // check pairing
1605       if (!markedIdle) {
1606         markedIdle = true;
1607       }
1608       beginIdleBlockTime = pool->pool[i].time;
1609     } else if (pool->pool[i].type == END_IDLE) {
1610       // check pairing
1611       if (markedIdle) {
1612         markedIdle = false;
1613         execTimes[numEvents] +=
1614           pool->pool[i].time - beginIdleBlockTime;
1615       }
1616     }
1617   }
1618   // convert all values to milliseconds first (else values would be too small
1619   // or too big)
1620   for (int i=0; i<numEvents; i++) {
1621     execTimes[i] *= 1.0e3;
1622   }
1623
1624   // compute squares
1625   for (int i=0; i<numEvents; i++) {
1626     execTimes[i+numEvents] = execTimes[i]*execTimes[i];
1627   }
1628
1629   CkCallback cb(CkIndex_TraceProjectionsBOC::outlierAverageReduction(NULL), 
1630                 0, thisProxy);
1631   contribute(numEvents*2*sizeof(double), execTimes, 
1632              CkReduction::sum_double, cb);  
1633 }
1634
1635 void TraceProjectionsBOC::outlierAverageReduction(CkReductionMsg *msg) {
1636   CkAssert(CkMyPe() == 0);
1637   // kinda wierd place to initialize a variable, but ...
1638   encounteredWeights = 0;
1639   weightArray = new double[CkNumPes()];
1640   mapArray = new int[CkNumPes()];
1641
1642   // calculate statistics
1643   int numEvents = _entryTable.size()+1;
1644   OutlierStatsMessage *outmsg = new (numEvents*2) OutlierStatsMessage;
1645
1646   double *execTimes = (double *)msg->getData();
1647   /*
1648   for (int i=0; i<numEvents; i++) {
1649     CkPrintf("EP: %d has Data: %lf and sum of squares %lf\n",
1650              i,execTimes[i],execTimes[i+numEvents]);
1651   }
1652   */
1653   for (int i=0; i<numEvents; i++) {
1654     // calculate mean
1655     outmsg->stats[i] = execTimes[i]/CkNumPes();
1656     // calculate stddev (using biased variance)
1657     outmsg->stats[i+numEvents] = 
1658       sqrt((execTimes[i+numEvents]-2*(outmsg->stats[i])*execTimes[i]+
1659             (outmsg->stats[i])*(outmsg->stats[i])*CkNumPes())/
1660            CkNumPes());
1661     // CkPrintf("EP:%d Mean:%lf Stddev:%lf\n",i,outmsg->stats[i],
1662     //       outmsg->stats[i+numEvents]);
1663   }
1664   delete msg;
1665
1666   // output averages to a file in microseconds. File handle to be kept
1667   // open so we can write more stats to it.
1668   char *fname = new char[strlen(CkpvAccess(traceRoot))+strlen(".outlier")+1];
1669   sprintf(fname, "%s.outlier", CkpvAccess(traceRoot));
1670   do {
1671     outlierfp = fopen(fname, "w");
1672   } while (!outlierfp && (errno == EINTR || errno == EMFILE));
1673   for (int i=0; i<numEvents; i++) {
1674     fprintf(outlierfp,"%lld ",(CMK_TYPEDEF_UINT8)(outmsg->stats[i]*1.0e3));
1675   }
1676   fprintf(outlierfp,"\n");
1677
1678   // broadcast statistical values to all processors for weight calculations
1679   thisProxy.calculateWeights(outmsg);
1680 }
1681
1682 void TraceProjectionsBOC::calculateWeights(OutlierStatsMessage *msg) {
1683   // calculate outlier "weights"
1684   int numEvents = _entryTable.size()+1;
1685
1686   // this is silly, but do it for now. First pass for computing total
1687   // time. It is also a misnomer, since this total will not include
1688   // any overhead time.
1689   OutlierWeightMessage *outmsg = new OutlierWeightMessage;
1690   weight = 0.0;
1691   outmsg->sourcePe = CkMyPe();
1692   outmsg->weight = 0.0;
1693   double total = 0.0;
1694   for (int i=0; i<numEvents; i++) {
1695     total += execTimes[i];
1696   }
1697   for (int i=0; i<numEvents; i++) {
1698     if ((total > 0.0) &&
1699         (msg->stats[i+numEvents] > 0.0)) {
1700       outmsg->weight += 
1701         (fabs(execTimes[i]-msg->stats[i])/msg->stats[i+numEvents]) *
1702         (msg->stats[i]/total);
1703     }
1704     // CkPrintf("[%d] EP:%d Weight:%lf\n",CkMyPe(),i,outmsg->weight);
1705   }
1706   weight = outmsg->weight;
1707   delete msg;
1708   
1709   thisProxy[0].determineOutliers(outmsg);
1710 }
1711
1712 void TraceProjectionsBOC::determineOutliers(OutlierWeightMessage *msg) {
1713   CkAssert(CkMyPe() == 0);
1714   encounteredWeights++;
1715
1716   // For now, implement a full array for sorting later. For better scaling
1717   // it should really be a sorted list of maximum k entries.
1718   weightArray[msg->sourcePe] = msg->weight;
1719
1720   if (encounteredWeights == CkNumPes()) {
1721     OutlierThresholdMessage *outmsg = new OutlierThresholdMessage;
1722     outmsg->threshold = 0.0;
1723
1724     // initialize the map array
1725     for (int i=0; i<CkNumPes(); i++) {
1726       mapArray[i] = i;
1727     }
1728     
1729     // bubble sort the array
1730     for (int p=CkNumPes()-1; p>0; p--) {
1731       for (int i=0; i<p; i++) {
1732         if (weightArray[i+1] < weightArray[i]) {
1733           int tempI;
1734           double temp = weightArray[i+1];
1735           weightArray[i+1] = weightArray[i];
1736           weightArray[i] = temp;
1737           tempI = mapArray[i+1];
1738           mapArray[i+1] = mapArray[i];
1739           mapArray[i] = tempI;
1740         }
1741       }
1742     }    
1743
1744     // default threshold is applied.
1745     //
1746     // **CW** This needs to be changed to accept a runtime parameter
1747     // so the default can be overridden. Default value considers the
1748     // top 10% "different" processors as outliers.
1749     int thresholdIndex;
1750     thresholdIndex = (int)(CkNumPes()*0.9);
1751     if (thresholdIndex == CkNumPes()) {
1752       thresholdIndex--;
1753     }
1754
1755     // output the sorted processor list to stats file
1756     for (int i=thresholdIndex; i<CkNumPes(); i++) {
1757       fprintf(outlierfp,"%d ",mapArray[i]);
1758     }
1759     fprintf(outlierfp,"\n");
1760     fflush(outlierfp);
1761     fclose(outlierfp);
1762
1763     // CkPrintf("Outlier Index determined to be %d with value %lf\n",
1764     //       thresholdIndex, weightArray[thresholdIndex]);
1765     outmsg->threshold = weightArray[thresholdIndex];
1766     
1767     delete msg;
1768     thisProxy.setOutliers(outmsg);
1769   } else {
1770     delete msg;
1771   }
1772 }
1773
1774 void TraceProjectionsBOC::setOutliers(OutlierThresholdMessage *msg)
1775 {
1776   LogPool *pool = CkpvAccess(_trace)->_logPool;
1777   // CkPrintf("[%d] My weight is %lf, threshold is %lf\n",CkMyPe(),weight,
1778   //   msg->threshold);
1779   if (weight < msg->threshold) {
1780     // CkPrintf("[%d] Removing myself from output list\n");
1781     pool->writeData = false;
1782   }
1783
1784   delete msg;
1785   thisProxy[CkMyPe()].startEndTimeAnalysis();
1786 }
1787
1788 void TraceProjectionsBOC::startEndTimeAnalysis()
1789 {
1790   endTime = CkpvAccess(_trace)->endTime;
1791   // CkPrintf("[%d] End time is %lf us\n", CkMyPe(), endTime*1e06);
1792
1793   CkCallback cb(CkIndex_TraceProjectionsBOC::endTimeReduction(NULL), 
1794                 0, thisProxy);
1795   contribute(sizeof(double), &endTime, CkReduction::max_double, cb);  
1796 }
1797
1798 void TraceProjectionsBOC::endTimeReduction(CkReductionMsg *msg)
1799 {
1800   CkAssert(CkMyPe() == 0);
1801   if (CkpvAccess(_trace) != NULL) {
1802     CkpvAccess(_trace)->_logPool->globalEndTime = *(double *)msg->getData();
1803     // CkPrintf("End time determined to be %lf us\n",
1804     //       (CkpvAccess(_trace)->_logPool->globalEndTime)*1e06);
1805   }
1806   delete msg;  
1807   CkPrintf("Total Analysis Time = %lf\n", CmiWallTimer()-analysisStartTime);
1808   thisProxy.closeTrace();
1809 }
1810
1811 void TraceProjectionsBOC::finalReduction(CkReductionMsg *msg)
1812 {
1813   CkAssert(CkMyPe() == 0);
1814   // CkPrintf("Final Reduction called\n");
1815   delete msg;
1816   CkExit();
1817 }
1818
1819 void TraceProjectionsBOC::closeTrace()
1820 {
1821   if (CkpvAccess(_trace) == NULL) {
1822     return;
1823   }
1824   CkpvAccess(_trace)->closeTrace();
1825   // the following section is only excuted if CkExit is called, since
1826   // closing-progress is required for other modules.
1827   if (!CkpvAccess(_trace)->converseExit) {
1828     // this is a dummy reduction to make sure that all the output
1829     // is completed before other modules are allowed to do stuff
1830     // and finally _CkExit is called.
1831     CkCallback cb(CkIndex_TraceProjectionsBOC::finalReduction(NULL), 
1832                   0, thisProxy);
1833     contribute(sizeof(double), &dummy, CkReduction::max_double, cb);
1834   }
1835 }
1836
1837 // This is the C++ code that is registered to be activated at module
1838 // shutdown. This is called exactly once on processor 0.
1839 // 
1840 extern "C" void CombineProjections()
1841 {
1842 #ifndef CMK_OPTIMIZE
1843   // CkPrintf("[%d] CombineProjections called!\n", CkMyPe());
1844   CProxy_TraceProjectionsBOC bocProxy(traceProjectionsGID);
1845   bocProxy.shutdownAnalysis();
1846 #else
1847   CkExit();
1848 #endif
1849 }
1850
1851 // This method is called by module initialization to register the exit
1852 // function. This exit function must ultimately call CkExit() again to
1853 // so that other module exit functions may proceed after this module is
1854 // done.
1855 //
1856 // This is called once on each processor but the idiom of use appears
1857 // to be to only have processor 0 register the function.
1858 void initTraceProjectionsBOC()
1859 {
1860   // CkPrintf("[%d] Trace Projections initialization called!\n", CkMyPe());
1861 #ifdef __BLUEGENE__
1862   if (BgNodeRank() == 0) {
1863 #else
1864     if (CkMyRank() == 0) {
1865 #endif
1866       registerExitFn(CombineProjections);
1867     }
1868   }
1869
1870 #include "TraceProjections.def.h"
1871 #endif
1872
1873 /*@}*/