Dynamically allocating the tix box breakpoint array
[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 #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;
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 // Allocate the tixBox breakpoint array
511 // These are set to 1 if you want to
512 // stop at a specific breakpoint
513 tixBoxBP = (int *)calloc(1,sizeof(int));
514 }
515
516 }
517
518
519 /* Called at the end of execution, to write out the Hpc *.tix file
520 * for this exection. Safe to call, even if coverage is not used.
521 */
522 void
523 exitHpc(void) {
524 Info *tmpModule;
525 int i, comma;
526
527 #if DEBUG_HPC
528 fprintf(stderr,"exitHpc\n");
529 #endif
530
531 if (hpc_inited == 0) {
532 return;
533 }
534
535 FILE *f = fopen(tixFilename,"w");
536
537 comma = 0;
538
539 fprintf(f,"Tix %" PRIuWORD64 " [", magicTixNumber);
540 tmpModule = modules;
541 for(;tmpModule != 0;tmpModule = tmpModule->next) {
542 if (comma) {
543 fprintf(f,",");
544 } else {
545 comma = 1;
546 }
547 fprintf(f,"(\"%s\",%u)",
548 tmpModule->modName,
549 tmpModule->tickCount);
550 #if DEBUG_HPC
551 fprintf(stderr,"%s: %u (offset=%u)\n",
552 tmpModule->modName,
553 tmpModule->tickCount,
554 tmpModule->tickOffset);
555 #endif
556 }
557 fprintf(f,"] [");
558
559 comma = 0;
560 tmpModule = modules;
561 for(;tmpModule != 0;tmpModule = tmpModule->next) {
562 if (!tmpModule->tixArr) {
563 fprintf(stderr,"warning: module %s did not register any hpc tick data\n",
564 tmpModule->modName);
565 }
566
567 for(i = 0;i < tmpModule->tickCount;i++) {
568 if (comma) {
569 fprintf(f,",");
570 } else {
571 comma = 1;
572 }
573
574 if (tmpModule->tixArr) {
575 fprintf(f,"%" PRIuWORD64,tmpModule->tixArr[i]);
576 } else {
577 fprintf(f,"0");
578 }
579
580 }
581 }
582
583 fprintf(f,"]\n");
584 fclose(f);
585
586 if (rixFile != NULL) {
587 hs_hpc_tick(RixFinishedOp,(StgThreadID)0);
588 fclose(rixFile);
589 }
590 if (rixCmdFile != NULL) {
591 fclose(rixCmdFile);
592 }
593
594 }
595