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