Changing internal data structures used by Hpc
[ghc-hetmet.git] / rts / Hpc.c
1 /*
2  * (c)2006 Galois Connections, Inc.
3  */ 
4
5 #include <stdio.h>
6 #include <ctype.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h>
10
11 #include "Rts.h"
12 #include "Hpc.h"
13 #include "Trace.h"
14
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18
19
20 /* This is the runtime support for the Haskell Program Coverage (hpc) toolkit,
21  * inside GHC.
22  *
23  */
24
25 #define WOP_SIZE 1024   
26
27 static int hpc_inited = 0;              // Have you started this component?
28 static FILE *tixFile;                   // file being read/written
29 static int tix_ch;                      // current char
30
31 static FILE *rixFile = NULL;            // The tracer file/pipe (to debugger)
32 static FILE *rixCmdFile = NULL;         // The tracer file/pipe (from debugger)
33 static StgWord64 rixCounter = 0;        // The global event counter
34 static int debuggee_pid;
35
36 typedef enum {
37   RixThreadFinishedOp   = -1,
38   RixRaiseOp            = -2,
39   RixFinishedOp         = -3
40 } HpcRixOp;
41
42
43 typedef struct _Info {
44   char *modName;                // name of module
45   int tickCount;                // number of ticks
46   int tickOffset;               // offset into a single large .tix Array
47   int hashNo;                   // Hash number for this module's mix info
48   StgWord64 *tixArr;            // tix Array from the program execution (local for this module)
49   struct _Info *next;
50 } Info;
51
52 // This is a cruel hack, we should completely redesign the format specifier handling in the RTS.
53 #if SIZEOF_LONG == 8
54 #define PRIuWORD64 "lu"
55 #else
56 #define PRIuWORD64 "llu"
57 #endif
58
59 Info *modules = 0;
60 Info *nextModule = 0;
61 int totalTixes = 0;             // total number of tix boxes.
62
63 static char *tixFilename;
64
65 static void failure(char *msg) {
66   debugTrace(DEBUG_hpc,"hpc failure: %s\n",msg);
67   fprintf(stderr,"Hpc failure: %s\n",msg);
68   if (tixFilename) {
69     fprintf(stderr,"(perhaps remove %s file?)\n",tixFilename);
70   } else {
71     fprintf(stderr,"(perhaps remove .tix file?)\n");
72   }
73   exit(-1);
74 }
75
76 static int init_open(char *filename) 
77 {
78   tixFile = fopen(filename,"r");
79  if (tixFile == 0) {
80     return 0;
81   }
82   tix_ch = getc(tixFile);
83   return 1;
84 }
85
86 static void expect(char c) {
87   if (tix_ch != c) {
88     fprintf(stderr,"('%c' '%c')\n",tix_ch,c);
89     failure("parse error when reading .tix file");
90   }
91   tix_ch = getc(tixFile);
92 }
93
94 static void ws(void) {
95   while (tix_ch == ' ') {
96     tix_ch = getc(tixFile);
97   }
98 }
99
100 static char *expectString(void) {
101   char tmp[256], *res;
102   int tmp_ix = 0;
103   expect('"');
104   while (tix_ch != '"') {
105     tmp[tmp_ix++] = tix_ch;
106     tix_ch = getc(tixFile);
107   }
108   tmp[tmp_ix++] = 0;
109   expect('"');
110   res = malloc(tmp_ix);
111   strcpy(res,tmp);
112   return res;
113 }
114
115 static StgWord64 expectWord64(void) {
116   StgWord64 tmp = 0;
117   while (isdigit(tix_ch)) {
118     tmp = tmp * 10 + (tix_ch -'0');
119     tix_ch = getc(tixFile);
120   }
121   return tmp;
122 }
123
124 static void hpc_init(void) {
125   int i;
126   Info *tmpModule;  
127
128   if (hpc_inited != 0) {
129     return;
130   }
131   hpc_inited = 1;
132
133   tixFilename = (char *) malloc(strlen(prog_name) + 6);
134   sprintf(tixFilename, "%s.tix", prog_name);
135
136   if (init_open(tixFilename)) { 
137     totalTixes = 0;
138
139     ws();
140     expect('T');
141     expect('i');
142     expect('x');
143     ws();
144     expect('[');
145     ws();
146     while(tix_ch != ']') {
147       tmpModule = (Info *)calloc(1,sizeof(Info));
148       expect('T');
149       expect('i');
150       expect('x');
151       expect('M');
152       expect('o');
153       expect('d');
154       expect('u');
155       expect('l');
156       expect('e');
157       ws();
158       tmpModule -> modName = expectString();
159       ws();
160       tmpModule -> hashNo = (unsigned int)expectWord64();
161       ws();
162       tmpModule -> tickCount = (int)expectWord64();
163       tmpModule -> tixArr = (StgWord64 *)calloc(tmpModule->tickCount,sizeof(StgWord64));
164       tmpModule -> tickOffset = totalTixes;
165       totalTixes += tmpModule -> tickCount;
166       ws();
167       expect('[');
168       ws();
169       for(i = 0;i < tmpModule->tickCount;i++) {
170         tmpModule->tixArr[i] = expectWord64();
171         ws();
172         if (tix_ch == ',') {
173           expect(',');
174           ws();
175         }
176       }
177       expect(']');
178       ws();
179
180       if (!modules) {
181         modules = tmpModule;
182       } else {
183         nextModule->next=tmpModule;
184       }
185       nextModule=tmpModule;
186       
187       if (tix_ch == ',') {
188         expect(',');
189         ws();
190       }
191     }
192     expect(']');
193     fclose(tixFile);
194   }
195 }
196
197 /* Called on a per-module basis, at startup time, declaring where the tix boxes are stored in memory.
198  * This memory can be uninitized, because we will initialize it with either the contents
199  * of the tix file, or all zeros.
200  */
201
202 int
203 hs_hpc_module(char *modName,
204               int modCount,
205               int modHashNo,
206               StgWord64 *tixArr) {
207   Info *tmpModule, *lastModule;
208   int i;
209   int offset = 0;
210   
211   debugTrace(DEBUG_hpc,"hs_hpc_module(%s,%d)",modName,modCount);
212
213   hpc_init();
214
215   tmpModule = modules;
216   lastModule = 0;
217   
218   for(;tmpModule != 0;tmpModule = tmpModule->next) {
219     if (!strcmp(tmpModule->modName,modName)) {
220       if (tmpModule->tickCount != modCount) {
221         failure("inconsistent number of tick boxes");
222       }
223       assert(tmpModule->tixArr != 0);   
224       if (tmpModule->hashNo != modHashNo) {
225         fprintf(stderr,"in module '%s'\n",tmpModule->modName);
226         failure("module mismatch with .tix/.mix file hash number");
227         fprintf(stderr,"(perhaps remove %s ?)\n",tixFilename);
228         exit(-1);
229
230       }
231       for(i=0;i < modCount;i++) {
232         tixArr[i] = tmpModule->tixArr[i];
233       }
234       tmpModule->tixArr = tixArr;
235       return tmpModule->tickOffset;
236     }
237     lastModule = tmpModule;
238   }
239   // Did not find entry so add one on.
240   tmpModule = (Info *)calloc(1,sizeof(Info));
241   tmpModule->modName = modName;
242   tmpModule->tickCount = modCount;
243   tmpModule->hashNo = modHashNo;
244   if (lastModule) {
245     tmpModule->tickOffset = lastModule->tickOffset + lastModule->tickCount;
246   } else {
247     tmpModule->tickOffset = 0;
248   }
249   tmpModule->tixArr = tixArr;
250   for(i=0;i < modCount;i++) {
251     tixArr[i] = 0;
252   }
253   tmpModule->next = 0;
254
255   if (!modules) {
256     modules = tmpModule;
257   } else {
258     lastModule->next=tmpModule;
259   }
260
261   debugTrace(DEBUG_hpc,"end: hs_hpc_module");
262
263   return offset;
264 }
265
266 static void breakPointCommand(HpcRixOp rixOp, StgThreadID rixTid);
267
268 // Breakpointing
269 static StgThreadID previousTid = 0;
270 static StgWord64 rixBPCounter = 0;      // The global event breakpoint counter
271 static int *tixBoxBP;
272 static HpcRixOp rixOpBack[WOP_SIZE];    // The actual op
273 static HpcRixOp rixTidBack[WOP_SIZE];   // Tid's before the op
274
275 void 
276 hs_hpc_raise_event(StgTSO *current_tso) {
277   hs_hpc_tick(RixRaiseOp,current_tso);
278 }
279
280 void 
281 hs_hpc_thread_finished_event(StgTSO *current_tso) {
282   hs_hpc_tick(RixThreadFinishedOp,current_tso);
283 }
284
285 /* Called on every tick, dynamically, sending to our 
286  * external record of program execution.
287  */
288
289 void
290 hs_hpc_tick(int rixOp, StgTSO *current_tso) {
291
292   debugTrace(DEBUG_hpc,"hs_hpc_tick(%x)",rixOp);
293
294   if (rixFile == NULL) {
295     return;
296   }
297   assert(rixCmdFile != NULL);
298   StgThreadID tid = (current_tso == 0) ? 0 : current_tso->id;
299
300   // now check to see if we have met a breakpoint condition
301   if (rixCounter == rixBPCounter 
302       || tid != previousTid) {
303     breakPointCommand(rixOp,tid);
304   } else {
305     if (rixOp >= 0) {
306       // Tix op
307       if (tixBoxBP[rixOp] == 1) {       // reached a bp tixbox
308           breakPointCommand(rixOp,tid);
309       }
310     } else {
311       // record the special operation
312       breakPointCommand(rixOp,tid);
313     }
314   }
315   // update the history information.
316   previousTid = tid;
317   rixOpBack[rixCounter % WOP_SIZE]  = rixOp;
318   rixTidBack[rixCounter % WOP_SIZE] = tid;
319   rixCounter++;
320
321   debugTrace(DEBUG_hpc, "end: hs_hpc_tick");
322 }
323
324 static void 
325 printEvent(FILE *out,StgWord64 rixCounter,StgThreadID rixTid,HpcRixOp rixOp) {
326   char prefixMsg[128];
327   char suffixMsg[128];
328
329   sprintf(prefixMsg,
330           "Event %" PRIuWORD64 " %u ",
331           rixCounter,
332           (unsigned int)rixTid);
333
334   switch(rixOp) {
335   case RixThreadFinishedOp:
336     sprintf(suffixMsg,"ThreadFinished");
337     break;
338   case RixRaiseOp:
339     sprintf(suffixMsg,"Raise");
340     break;
341   case RixFinishedOp:
342     sprintf(suffixMsg,"Finished");
343     break;
344   default:
345     sprintf(suffixMsg,"%u",rixOp);
346   }
347
348   fprintf(out,"%s%s\n",prefixMsg,suffixMsg);
349   debugTrace(DEBUG_hpc,"sending %s%s",prefixMsg,suffixMsg);
350 }
351
352 static void
353 breakPointCommand(HpcRixOp rixOp, StgThreadID rixTid) {
354   StgWord64 tmp64 = 0;
355   unsigned int tmp = 0;
356
357   if (getpid() != debuggee_pid) {
358     // We are not the original process, to do not issue 
359     // any events, and do not try to talk to the debugger.
360     return;
361   }
362
363   debugTrace(DEBUG_hpc,"breakPointCommand %d %x",rixOp,(unsigned int)rixTid);
364
365   printEvent(rixFile,rixCounter,rixTid,rixOp);
366   fflush(rixFile);
367
368   /* From here, you can ask some basic questions.
369    * 
370    *  c<nat>            set the (one) counter breakpoint
371    *  s<nat>            set the (many) tickbox breakpoint
372    *  u<nat>            unset the (many) tickbox breakpoint
373    *  h                 history
374
375    * Note that you aways end up here on the first tick
376    * because the rixBPCounter starts equal to 0.
377    */
378   int c = getc(rixCmdFile);
379   while(c != 10 && c != -1) {
380     switch(c) {
381     case 'c': // c1234  -- set counter breakpoint at 1234
382       c = getc(rixCmdFile);
383       tmp64 = 0;
384       while(isdigit(c)) {
385         tmp64 = tmp64 * 10 + (c - '0');
386         c = getc(rixCmdFile);
387       }
388       debugTrace(DEBUG_hpc,"setting countBP = %" PRIuWORD64,tmp64);
389
390       rixBPCounter = tmp64;
391       break;
392     case 's': // s2323  -- set tick box breakpoint at 2323
393       c = getc(rixCmdFile);
394       tmp = 0;
395       while(isdigit(c)) {
396         tmp = tmp * 10 + (c - '0');
397         c = getc(rixCmdFile);
398       }
399
400       debugTrace(DEBUG_hpc,"seting bp for tix %d",tmp);
401
402       tixBoxBP[tmp] = 1;
403       break;
404     case 'u': // u2323  -- unset tick box breakpoint at 2323
405       c = getc(rixCmdFile);
406       tmp = 0;
407       while(isdigit(c)) {
408         tmp = tmp * 10 + (c - '0');
409         c = getc(rixCmdFile);
410       }
411
412       debugTrace(DEBUG_hpc,"unseting bp for tix %d",tmp);
413
414       tixBoxBP[tmp] = 0;
415       break;
416     case 'h': // h -- history of the last few (WOP_SIZE) steps 
417       if (rixCounter > WOP_SIZE) {
418         tmp64 = rixCounter - WOP_SIZE;
419       } else {
420         tmp64 = 0;
421       }
422       for(;tmp64 < rixCounter;tmp64++) {
423         printEvent(rixFile,
424                    tmp64,
425                    rixTidBack[tmp64 % WOP_SIZE],
426                    rixOpBack[tmp64 % WOP_SIZE]);
427       }
428       fflush(rixFile);
429       c = getc(rixCmdFile);
430       break;
431     default:
432
433       debugTrace(DEBUG_hpc,"strange command from HPCRIX (%d)",c);
434
435       c = getc(rixCmdFile);
436     }
437     while (c != 10) {          // the end of the line
438         c = getc(rixCmdFile); // to the end of the line
439     }
440     c = getc(rixCmdFile); // the first char on the next command
441   }
442
443   debugTrace(DEBUG_hpc,"leaving breakPointCommand");
444
445 }
446
447 /* This is called after all the modules have registered their local tixboxes,
448  * and does a sanity check: are we good to go?
449  */
450
451 void
452 startupHpc(void) {
453   char *hpcRix;
454
455   debugTrace(DEBUG_hpc,"startupHpc");
456  
457  if (hpc_inited == 0) {
458     return;
459   }
460   // HPCRIX contains the name of the file to send our dynamic runtime output to (a named pipe).
461
462   hpcRix = getenv("HPCRIX");
463   if (hpcRix) {
464     int comma;
465     Info *tmpModule;  
466     int rixFD, rixCmdFD;
467     int tixCount = 0;
468
469     assert(hpc_inited);
470
471     if (sscanf(hpcRix,"%d:%d",&rixFD,&rixCmdFD) != 2) {
472       /* Bad format for HPCRIX.
473        */
474       debugTrace(DEBUG_hpc,"Bad HPCRIX (%s)",hpcRix);
475       exit(0);
476     }
477
478     debugTrace(DEBUG_hpc,"found HPCRIX pipes: %d:%d",rixFD,rixCmdFD);
479
480     rixFile = fdopen(rixFD,"w");
481     assert(rixFile != NULL);
482
483     rixCmdFile = fdopen(rixCmdFD,"r");
484     assert(rixCmdFile != NULL);
485
486     // If we fork a process, then we do not want ticks inside
487     // the sub-process to talk to the debugger. So we remember
488     // our pid at startup time, so we can check if we are still
489     // the original process.
490
491     debuggee_pid = getpid();
492
493     comma = 0;
494     
495     fprintf(rixFile,"Starting %s\n",prog_name);
496     fprintf(rixFile,"[");
497     tmpModule = modules;
498     for(;tmpModule != 0;tmpModule = tmpModule->next) {
499       if (comma) {
500         fprintf(rixFile,",");
501       } else {
502         comma = 1;
503       }
504       fprintf(rixFile,"(\"%s\",%u)",
505               tmpModule->modName,
506               tmpModule->tickCount);
507
508       tixCount += tmpModule->tickCount;
509
510       debugTrace(DEBUG_hpc,"(tracer)%s: %u (offset=%u) (hash=%u)\n",
511                  tmpModule->modName,
512                  tmpModule->tickCount,
513                  tmpModule->hashNo,
514                  tmpModule->tickOffset);
515
516     }
517     fprintf(rixFile,"]\n");
518     fflush(rixFile);
519
520     // Allocate the tixBox breakpoint array
521     // These are set to 1 if you want to 
522     // stop at a specific breakpoint
523     tixBoxBP = (int *)calloc(tixCount,sizeof(int));
524   }
525
526 }
527
528
529 /* Called at the end of execution, to write out the Hpc *.tix file  
530  * for this exection. Safe to call, even if coverage is not used.
531  */
532 void
533 exitHpc(void) {
534   Info *tmpModule;  
535   int i, inner_comma, outer_comma;
536
537   debugTrace(DEBUG_hpc,"exitHpc");
538
539   if (hpc_inited == 0) {
540     return;
541   }
542
543   FILE *f = fopen(tixFilename,"w");
544   
545   outer_comma = 0;
546
547   fprintf(f,"Tix [");
548   tmpModule = modules;
549   for(;tmpModule != 0;tmpModule = tmpModule->next) {
550     if (outer_comma) {
551       fprintf(f,",");
552     } else {
553       outer_comma = 1;
554     }
555     fprintf(f," TixModule \"%s\" %u %u [",
556            tmpModule->modName,
557             tmpModule->hashNo,
558             tmpModule->tickCount);
559     debugTrace(DEBUG_hpc,"%s: %u (offset=%u) (hash=%u)\n",
560                tmpModule->modName,
561                tmpModule->tickCount,
562                tmpModule->hashNo,
563                tmpModule->tickOffset);
564
565     inner_comma = 0;
566     for(i = 0;i < tmpModule->tickCount;i++) {
567       if (inner_comma) {
568         fprintf(f,",");
569       } else {
570         inner_comma = 1;
571       }
572
573       if (tmpModule->tixArr) {
574         fprintf(f,"%" PRIuWORD64,tmpModule->tixArr[i]);
575       } else {
576         fprintf(f,"0");
577       }
578     }
579     fprintf(f,"]");
580   }
581   fprintf(f,"]\n");
582   
583   /*
584   tmpModule = modules;
585   for(;tmpModule != 0;tmpModule = tmpModule->next) {
586       if (!tmpModule->tixArr) {
587         debugTrace(DEBUG_hpc,
588                    "warning: module %s did not register any hpc tick data\n",
589                    tmpModule->modName);
590       }
591
592     for(i = 0;i < tmpModule->tickCount;i++) {
593       if (comma) {
594         fprintf(f,",");
595       } else {
596         comma = 1;
597       }
598
599       if (tmpModule->tixArr) {
600         fprintf(f,"%" PRIuWORD64,tmpModule->tixArr[i]);
601       } else {
602         fprintf(f,"0");
603       }
604
605     }
606   }
607       
608   fprintf(f,"]\n");
609   */
610   fclose(f);
611
612   if (rixFile != NULL) {
613     hs_hpc_tick(RixFinishedOp,(StgThreadID)0);
614     fclose(rixFile);
615   }
616   if (rixCmdFile != NULL) {
617     fclose(rixCmdFile);
618   }
619   
620 }
621