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