Updating rix output to new standard.
[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 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 = tid;
255 // How do we print StgWord32's without a cast?
256 fprintf(rixFile,"Thread Switch %d\n",(unsigned int)tid);
257 }
258 }
259
260 /*
261 * Called on *every* exception thrown
262 */
263 void
264 hs_hpc_event(char *msg,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,"%s\n",msg);
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