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