14f9d80226376a830a6aaefc1c84900c3ae17023
[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
22 static int hpc_inited = 0;              // Have you started this component?
23 static FILE *tixFile;                   // file being read/written
24 static int tix_ch;                      // current char
25 static StgWord64 magicTixNumber;        // Magic/Hash number to mark .tix files
26
27 static FILE *rixFile = NULL;            // The tracer file/pipe
28
29 typedef struct _Info {
30   char *modName;                // name of module
31   int tickCount;                // number of ticks
32   int tickOffset;               // offset into a single large .tix Array
33   StgWord64 *tixArr;            // tix Array from the program execution (local for this module)
34   struct _Info *next;
35 } Info;
36
37 // This is a cruel hack, we should completely redesign the format specifier handling in the RTS.
38 #if SIZEOF_LONG == 8
39 #define PRIuWORD64 "lu"
40 #else
41 #define PRIuWORD64 "llu"
42 #endif
43
44 Info *modules = 0;
45 Info *nextModule = 0;
46 StgWord64 *tixBoxes = 0;        // local copy of tixBoxes array, from file.
47 int totalTixes = 0;             // total number of tix boxes.
48
49
50
51 static char *tixFilename;
52
53 static void failure(char *msg) {
54   printf("Hpc failure: %s\n",msg);
55   printf("(perhaps remove .tix file?)\n");
56   exit(-1);
57 }
58
59
60 static int init_open(char *filename) 
61 {
62   tixFile = fopen(filename,"r");
63  if (tixFile == 0) {
64     return 0;
65   }
66   tix_ch = getc(tixFile);
67   return 1;
68 }
69
70 static void expect(char c) {
71   if (tix_ch != c) {
72     printf("Hpc: parse failed (%c,%c)\n",tix_ch,c);
73     exit(-1);
74   }
75   tix_ch = getc(tixFile);
76 }
77
78 static void ws(void) {
79   while (tix_ch == ' ') {
80     tix_ch = getc(tixFile);
81   }
82 }
83
84 static char *expectString(void) {
85   char tmp[256], *res;
86   int tmp_ix = 0;
87   expect('"');
88   while (tix_ch != '"') {
89     tmp[tmp_ix++] = tix_ch;
90     tix_ch = getc(tixFile);
91   }
92   tmp[tmp_ix++] = 0;
93   expect('"');
94   res = malloc(tmp_ix);
95   strcpy(res,tmp);
96   return res;
97 }
98
99 static StgWord64 expectWord64(void) {
100   StgWord64 tmp = 0;
101   while (isdigit(tix_ch)) {
102     tmp = tmp * 10 + (tix_ch -'0');
103     tix_ch = getc(tixFile);
104   }
105   return tmp;
106 }
107
108 static void hpc_init(void) {
109   int i;
110   Info *tmpModule;  
111
112   if (hpc_inited != 0) {
113     return;
114   }
115   hpc_inited = 1;
116   
117
118   tixFilename = (char *) malloc(strlen(prog_name) + 6);
119   sprintf(tixFilename, "%s.tix", prog_name);
120
121   if (init_open(tixFilename)) { 
122     totalTixes = 0;
123
124     ws();
125     expect('T');
126     expect('i');
127     expect('x');
128     ws();
129     magicTixNumber = expectWord64();
130     ws();
131     expect('[');
132     ws();
133     while(tix_ch != ']') {
134       tmpModule = (Info *)calloc(1,sizeof(Info));
135       expect('(');
136       ws();
137       tmpModule -> modName = expectString();
138       ws();
139       expect(',');
140       ws();
141       tmpModule -> tickCount = (int)expectWord64();
142       ws();
143       expect(')');
144       ws();
145       
146       tmpModule -> tickOffset = totalTixes;
147       totalTixes += tmpModule -> tickCount;
148       
149       tmpModule -> tixArr = 0;
150       
151       if (!modules) {
152         modules = tmpModule;
153       } else {
154         nextModule->next=tmpModule;
155       }
156       nextModule=tmpModule;
157       
158       if (tix_ch == ',') {
159         expect(',');
160         ws();
161       }
162     }
163     expect(']');
164     ws();
165     tixBoxes = (StgWord64 *)calloc(totalTixes,sizeof(StgWord64));
166
167     expect('[');
168     for(i = 0;i < totalTixes;i++) {
169       if (i != 0) {
170         expect(',');
171         ws();
172       }
173     tixBoxes[i] = expectWord64();
174     ws();
175     }
176     expect(']');
177
178     fclose(tixFile);
179   } else {
180     // later, we will find a binary specific 
181     magicTixNumber = (StgWord64)0;
182   }
183 }
184
185 /* Called on a per-module basis, at startup time, declaring where the tix boxes are stored in memory.
186  * This memory can be uninitized, because we will initialize it with either the contents
187  * of the tix file, or all zeros.
188  */
189
190 int
191 hs_hpc_module(char *modName,int modCount,StgWord64 *tixArr) {
192   Info *tmpModule, *lastModule;
193   int i;
194   int offset = 0;
195   
196 #if DEBUG_HPC
197   printf("hs_hpc_module(%s,%d)\n",modName,modCount);
198 #endif
199
200   hpc_init();
201
202   tmpModule = modules;
203   lastModule = 0;
204   
205   for(;tmpModule != 0;tmpModule = tmpModule->next) {
206     if (!strcmp(tmpModule->modName,modName)) {
207       if (tmpModule->tickCount != modCount) {
208         failure("inconsistent number of tick boxes");
209       }
210       assert(tmpModule->tixArr == 0);   
211       assert(tixBoxes != 0);
212       tmpModule->tixArr = tixArr;
213       for(i=0;i < modCount;i++) {
214         tixArr[i] = tixBoxes[i + tmpModule->tickOffset];
215       }
216       return tmpModule->tickOffset;
217     }
218     lastModule = tmpModule;
219   }
220   // Did not find entry so add one on.
221   tmpModule = (Info *)calloc(1,sizeof(Info));
222   tmpModule->modName = modName;
223   tmpModule->tickCount = modCount;
224   if (lastModule) {
225     tmpModule->tickOffset = lastModule->tickOffset + lastModule->tickCount;
226   } else {
227     tmpModule->tickOffset = 0;
228   }
229   tmpModule->tixArr = tixArr;
230   for(i=0;i < modCount;i++) {
231     tixArr[i] = 0;
232   }
233   tmpModule->next = 0;
234
235   if (!modules) {
236     modules = tmpModule;
237   } else {
238     lastModule->next=tmpModule;
239   }
240
241 #if DEBUG_HPC
242   printf("end: hs_hpc_module\n");
243 #endif
244   return offset;
245 }
246
247 static StgThreadID previous_tid = 0;
248
249 static void 
250 send_ThreadId(StgTSO *current_tso) {
251   // This assumes that there is no real thread 0.
252   StgThreadID tid = (current_tso == 0) ? 0 : current_tso->id;
253   if (tid != previous_tid) {
254     previous_tid = current_tso->id;
255     // How do we print StgWord32's without a cast?
256     fprintf(rixFile,"Thread %d\n",(unsigned int)tid);
257   }
258 }
259
260 /*
261  * Called on *every* exception thrown
262  */
263 void
264 hs_hpc_throw(StgTSO *current_tso) {
265   // Assumes that we have had at least *one* tick first.
266   // All exceptions before the first tick are not reported.
267   // The only time this might be an issue is in bootstrapping code,
268   // so this is a feature.
269
270   // This is called on *every* exception, even when Hpc is not enabled.
271
272   if (rixFile != NULL) {
273     assert(hpc_inited != 0);
274     send_ThreadId(current_tso);
275     fprintf(rixFile,"Throw\n");
276   }
277 }
278
279 /* Called on every tick, dynamically to our file record of program execution
280  */
281
282 void
283 hs_hpc_tick(int globIx, StgTSO *current_tso) {
284 #if DEBUG_HPC && DEBUG
285   printf("hs_hpc_tick(%d)\n",globIx);
286 #endif
287   assert(hpc_inited != 0);
288   if (rixFile != NULL) {
289     send_ThreadId(current_tso);
290     fprintf(rixFile,"%d\n",globIx);
291   }
292
293 #if DEBUG_HPC
294   printf("end: hs_hpc_tick\n");
295 #endif
296   
297 }
298
299 /* This is called after all the modules have registered their local tixboxes,
300  * and does a sanity check: are we good to go?
301  */
302
303 void
304 startupHpc(void) {
305   Info *tmpModule;
306   char *hpcRix;
307 #if DEBUG_HPC
308   printf("startupHpc\n");
309 #endif
310  
311  if (hpc_inited == 0) {
312     return;
313   }
314
315   tmpModule = modules;
316
317   if (tixBoxes) {
318     for(;tmpModule != 0;tmpModule = tmpModule->next) {
319       if (!tmpModule->tixArr) {
320         fprintf(stderr,"error: module %s did not register any hpc tick data\n",
321                 tmpModule->modName);
322         fprintf(stderr,"(perhaps remove %s ?)\n",tixFilename);
323         exit(-1);
324       }
325     }
326   }
327
328   // HPCRIX contains the name of the file to send our dynamic runtime output to.
329   // This might be a real file, or perhaps a named pipe.
330   hpcRix = getenv("HPCRIX");
331   if (hpcRix) {
332     int comma;
333     Info *tmpModule;  
334
335     assert(hpc_inited);
336
337     rixFile = fopen(hpcRix,"w");
338
339     comma = 0;
340     
341     fprintf(rixFile,"Starting %s\n",prog_name);
342     fprintf(rixFile,"[");
343     tmpModule = modules;
344     for(;tmpModule != 0;tmpModule = tmpModule->next) {
345       if (comma) {
346         fprintf(rixFile,",");
347       } else {
348         comma = 1;
349       }
350       fprintf(rixFile,"(\"%s\",%u)",
351               tmpModule->modName,
352               tmpModule->tickCount);
353 #if DEBUG_HPC
354       fprintf(stderr,"(tracer)%s: %u (offset=%u)\n",
355               tmpModule->modName,
356               tmpModule->tickCount,
357               tmpModule->tickOffset);
358 #endif
359     }
360     fprintf(rixFile,"]\n");
361     fflush(rixFile);
362   }
363
364 }
365
366
367 /* Called at the end of execution, to write out the Hpc *.tix file  
368  * for this exection. Safe to call, even if coverage is not used.
369  */
370 void
371 exitHpc(void) {
372   Info *tmpModule;  
373   int i, comma;
374
375 #if DEBUG_HPC
376   printf("exitHpc\n");
377 #endif
378
379   if (hpc_inited == 0) {
380     return;
381   }
382
383   FILE *f = fopen(tixFilename,"w");
384   
385   comma = 0;
386
387   fprintf(f,"Tix %" PRIuWORD64 " [", magicTixNumber);
388   tmpModule = modules;
389   for(;tmpModule != 0;tmpModule = tmpModule->next) {
390     if (comma) {
391       fprintf(f,",");
392     } else {
393       comma = 1;
394     }
395     fprintf(f,"(\"%s\",%u)",
396            tmpModule->modName,
397             tmpModule->tickCount);
398 #if DEBUG_HPC
399     fprintf(stderr,"%s: %u (offset=%u)\n",
400            tmpModule->modName,
401            tmpModule->tickCount,
402            tmpModule->tickOffset);
403 #endif
404   }
405   fprintf(f,"] [");
406   
407   comma = 0;
408   tmpModule = modules;
409   for(;tmpModule != 0;tmpModule = tmpModule->next) {
410       if (!tmpModule->tixArr) {
411         fprintf(stderr,"warning: module %s did not register any hpc tick data\n",
412                 tmpModule->modName);
413       }
414
415     for(i = 0;i < tmpModule->tickCount;i++) {
416       if (comma) {
417         fprintf(f,",");
418       } else {
419         comma = 1;
420       }
421
422       if (tmpModule->tixArr) {
423         fprintf(f,"%" PRIuWORD64,tmpModule->tixArr[i]);
424       } else {
425         fprintf(f,"0");
426       }
427
428     }
429   }
430       
431   fprintf(f,"]\n");
432   fclose(f);
433
434   if (rixFile != NULL) {
435     fprintf(rixFile,"Finished\n");
436     fclose(rixFile);
437   }
438   
439 }
440