Haskell Program Coverage
[ghc-hetmet.git] / rts / Hpc.c
1 /*
2  * (c)2006 Galois Connections, Inc.
3  */ 
4
5 // #include "HsFFI.h"
6
7 #include <stdio.h>
8 #include <ctype.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <assert.h>
12 #include "HsFFI.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
26 typedef struct _Info {
27   char *modName;                // name of module
28   int tickCount;                // number of ticks
29   int tickOffset;               // offset into a single large .tix Array
30   StgWord64 *tixArr;            // tix Array from the program execution (local for this module)
31   struct _Info *next;
32 } Info;
33
34 Info *modules = 0;
35 Info *nextModule = 0;
36 StgWord64 *tixBoxes = 0;        // local copy of tixBoxes array, from file.
37 int totalTixes = 0;             // total number of tix boxes.
38
39
40
41 static char *tixFilename = "Main.tix";
42
43 static void failure(char *msg) {
44   printf("Hpc failure: %s\n",msg);
45   printf("(perhaps remove .tix file?)\n");
46   exit(-1);
47 }
48
49
50 static int init_open(char *filename) 
51 {
52   tixFile = fopen(filename,"r");
53  if (tixFile == 0) {
54     return 0;
55   }
56   tix_ch = getc(tixFile);
57   return 1;
58 }
59
60 static void expect(char c) {
61   if (tix_ch != c) {
62     printf("Hpc: parse failed (%c,%c)\n",tix_ch,c);
63     exit(-1);
64   }
65   tix_ch = getc(tixFile);
66 }
67
68 static void ws(void) {
69   while (tix_ch == ' ') {
70     tix_ch = getc(tixFile);
71   }
72 }
73
74 static char *expectString(void) {
75   char tmp[256], *res;
76   int tmp_ix = 0;
77   expect('"');
78   while (tix_ch != '"') {
79     tmp[tmp_ix++] = tix_ch;
80     tix_ch = getc(tixFile);
81   }
82   tmp[tmp_ix++] = 0;
83   expect('"');
84   res = malloc(tmp_ix);
85   strcpy(res,tmp);
86   return res;
87 }
88
89 static StgWord64 expectWord64(void) {
90   StgWord64 tmp = 0;
91   while (isdigit(tix_ch)) {
92     tmp = tmp * 10 + (tix_ch -'0');
93     tix_ch = getc(tixFile);
94   }
95   return tmp;
96 }
97
98 static void hpc_init(void) {
99   int i;
100   Info *tmpModule;  
101
102   if (hpc_inited != 0) {
103     return;
104   }
105   hpc_inited = 1;
106   
107   if (init_open(tixFilename)) { 
108     totalTixes = 0;
109
110     ws();
111     expect('T');
112     expect('i');
113     expect('x');
114     ws();
115     expectWord64();
116     ws();
117     expect('[');
118     ws();
119     while(tix_ch != ']') {
120       tmpModule = (Info *)calloc(1,sizeof(Info));
121       expect('(');
122       ws();
123       tmpModule -> modName = expectString();
124       ws();
125       expect(',');
126       ws();
127       tmpModule -> tickCount = (int)expectWord64();
128       ws();
129       expect(')');
130       ws();
131       
132       tmpModule -> tickOffset = totalTixes;
133       totalTixes += tmpModule -> tickCount;
134       
135       tmpModule -> tixArr = 0;
136       
137       if (!modules) {
138         modules = tmpModule;
139       } else {
140         nextModule->next=tmpModule;
141       }
142       nextModule=tmpModule;
143       
144       if (tix_ch == ',') {
145         expect(',');
146         ws();
147       }}
148     expect(']');
149     ws();
150     tixBoxes = (StgWord64 *)calloc(totalTixes,sizeof(StgWord64));
151
152     expect('[');
153     for(i = 0;i < totalTixes;i++) {
154       if (i != 0) {
155         expect(',');
156         ws();
157       }
158     tixBoxes[i] = expectWord64();
159     ws();
160     }
161     expect(']');
162
163     fclose(tixFile);
164   }
165 }
166
167 /* Called on a per-module basis, at startup time, declaring where the tix boxes are stored in memory.
168  * This memory can be uninitized, because we will initialize it with either the contents
169  * of the tix file, or all zeros.
170  */
171
172 void
173 hs_hpc_module(char *modName,int modCount,StgWord64 *tixArr) {
174   Info *tmpModule, *lastModule;
175   int i;
176   
177 #if DEBUG_HPC
178   printf("hs_hpc_module(%s,%d)\n",modName,modCount);
179 #endif
180
181   hpc_init();
182
183   tmpModule = modules;
184   lastModule = 0;
185   
186   for(;tmpModule != 0;tmpModule = tmpModule->next) {
187     if (!strcmp(tmpModule->modName,modName)) {
188       if (tmpModule->tickCount != modCount) {
189         failure("inconsistent number of tick boxes");
190       }
191       assert(tmpModule->tixArr == 0);   
192       assert(tixBoxes != 0);
193       tmpModule->tixArr = tixArr;
194       for(i=0;i < modCount;i++) {
195         tixArr[i] = tixBoxes[i + tmpModule->tickOffset];
196       }
197       return;
198     }
199     lastModule = tmpModule;
200   }
201   // Did not find entry so add one on.
202   tmpModule = (Info *)calloc(1,sizeof(Info));
203   tmpModule->modName = modName;
204   tmpModule->tickCount = modCount;
205   if (lastModule) {
206     tmpModule->tickOffset = lastModule->tickOffset + lastModule->tickCount;
207   } else {
208     tmpModule->tickOffset = 0;
209   }
210   tmpModule->tixArr = tixArr;
211   for(i=0;i < modCount;i++) {
212     tixArr[i] = 0;
213   }
214   tmpModule->next = 0;
215
216   if (!modules) {
217     modules = tmpModule;
218   } else {
219     lastModule->next=tmpModule;
220   }
221
222 #if DEBUG_HPC
223   printf("end: hs_hpc_module\n");
224 #endif
225 }
226
227 /* This is called after all the modules have registered their local tixboxes,
228  * and does a sanity check: are we good to go?
229  */
230
231 void
232 startupHpc(void) {
233   Info *tmpModule;
234 #if DEBUG_HPC
235   printf("startupHpc\n");
236 #endif
237  
238  if (hpc_inited == 0) {
239     return;
240   }
241
242   tmpModule = modules;
243
244   if (tixBoxes) {
245     for(;tmpModule != 0;tmpModule = tmpModule->next) {
246       if (!tmpModule->tixArr) {
247         fprintf(stderr,"error: module %s did not register any hpc tick data\n",
248                 tmpModule->modName);
249         fprintf(stderr,"(perhaps remove %s ?)\n",tixFilename);
250         exit(-1);
251       }
252     }
253   }
254 }
255
256 /* Called at the end of execution, to write out the Hpc *.tix file  
257  * for this exection. Safe to call, even if coverage is not used.
258  */
259 void
260 exitHpc(void) {
261   Info *tmpModule;  
262   int i, comma;
263
264 #if DEBUG_HPC
265   printf("exitHpc\n");
266 #endif
267
268   if (hpc_inited == 0) {
269     return;
270   }
271
272   FILE *f = fopen(tixFilename,"w");
273   
274   comma = 0;
275
276   fprintf(f,"Tix 0 [");
277   tmpModule = modules;
278   for(;tmpModule != 0;tmpModule = tmpModule->next) {
279     if (comma) {
280       fprintf(f,",");
281     } else {
282       comma = 1;
283     }
284     fprintf(f,"(\"%s\",%d)",
285            tmpModule->modName,
286             tmpModule->tickCount);
287 #if DEBUG_HPC
288     fprintf(stderr,"%s: %d (offset=%d)\n",
289            tmpModule->modName,
290            tmpModule->tickCount,
291            tmpModule->tickOffset);
292 #endif
293   }
294   fprintf(f,"] [");
295   
296   comma = 0;
297   tmpModule = modules;
298   for(;tmpModule != 0;tmpModule = tmpModule->next) {
299       if (!tmpModule->tixArr) {
300         fprintf(stderr,"warning: module %s did not register any hpc tick data\n",
301                 tmpModule->modName);
302       }
303
304     for(i = 0;i < tmpModule->tickCount;i++) {
305       if (comma) {
306         fprintf(f,",");
307       } else {
308         comma = 1;
309       }
310
311       if (tmpModule->tixArr) {
312         fprintf(f,"%lld",tmpModule->tixArr[i]);
313       } else {
314         fprintf(f,"0");
315       }
316
317     }
318   }
319       
320   fprintf(f,"]\n");
321   fclose(f);
322   
323 }
324