2 * (c)2006 Galois Connections, Inc.
15 /* This is the runtime support for the Haskell Program Coverage (hpc) toolkit,
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
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
35 RixThreadFinishedOp = -1,
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)
49 // This is a cruel hack, we should completely redesign the format specifier handling in the RTS.
51 #define PRIuWORD64 "lu"
53 #define PRIuWORD64 "llu"
58 StgWord64 *tixBoxes = 0; // local copy of tixBoxes array, from file.
59 int totalTixes = 0; // total number of tix boxes.
61 static char *tixFilename;
64 static void failure(char *msg) {
65 fprintf(stderr,"Hpc failure: %s\n",msg);
66 fprintf(stderr,"(perhaps remove .tix file?)\n");
71 static int init_open(char *filename)
73 tixFile = fopen(filename,"r");
77 tix_ch = getc(tixFile);
81 static void expect(char c) {
83 fprintf(stderr,"Hpc: parse failed (%c,%c)\n",tix_ch,c);
86 tix_ch = getc(tixFile);
89 static void ws(void) {
90 while (tix_ch == ' ') {
91 tix_ch = getc(tixFile);
95 static char *expectString(void) {
99 while (tix_ch != '"') {
100 tmp[tmp_ix++] = tix_ch;
101 tix_ch = getc(tixFile);
105 res = malloc(tmp_ix);
110 static StgWord64 expectWord64(void) {
112 while (isdigit(tix_ch)) {
113 tmp = tmp * 10 + (tix_ch -'0');
114 tix_ch = getc(tixFile);
119 static void hpc_init(void) {
123 if (hpc_inited != 0) {
129 tixFilename = (char *) malloc(strlen(prog_name) + 6);
130 sprintf(tixFilename, "%s.tix", prog_name);
132 if (init_open(tixFilename)) {
140 magicTixNumber = expectWord64();
144 while(tix_ch != ']') {
145 tmpModule = (Info *)calloc(1,sizeof(Info));
148 tmpModule -> modName = expectString();
152 tmpModule -> tickCount = (int)expectWord64();
157 tmpModule -> tickOffset = totalTixes;
158 totalTixes += tmpModule -> tickCount;
160 tmpModule -> tixArr = 0;
165 nextModule->next=tmpModule;
167 nextModule=tmpModule;
176 tixBoxes = (StgWord64 *)calloc(totalTixes,sizeof(StgWord64));
179 for(i = 0;i < totalTixes;i++) {
184 tixBoxes[i] = expectWord64();
191 // later, we will find a binary specific
192 magicTixNumber = (StgWord64)0;
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.
202 hs_hpc_module(char *modName,int modCount,StgWord64 *tixArr) {
203 Info *tmpModule, *lastModule;
208 fprintf(stderr,"hs_hpc_module(%s,%d)\n",modName,modCount);
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");
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];
227 return tmpModule->tickOffset;
229 lastModule = tmpModule;
231 // Did not find entry so add one on.
232 tmpModule = (Info *)calloc(1,sizeof(Info));
233 tmpModule->modName = modName;
234 tmpModule->tickCount = modCount;
236 tmpModule->tickOffset = lastModule->tickOffset + lastModule->tickCount;
238 tmpModule->tickOffset = 0;
240 tmpModule->tixArr = tixArr;
241 for(i=0;i < modCount;i++) {
249 lastModule->next=tmpModule;
253 fprintf(stderr,"end: hs_hpc_module\n");
258 static void breakPointCommand(HpcRixOp rixOp, StgThreadID rixTid);
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
269 hs_hpc_raise_event(StgTSO *current_tso) {
270 hs_hpc_tick(RixRaiseOp,current_tso);
274 hs_hpc_thread_finished_event(StgTSO *current_tso) {
275 hs_hpc_tick(RixThreadFinishedOp,current_tso);
278 /* Called on every tick, dynamically, sending to our
279 * external record of program execution.
283 hs_hpc_tick(int rixOp, StgTSO *current_tso) {
285 fprintf(stderr,"hs_hpc_tick(%x)\n",rixOp);
287 if (rixFile == NULL) {
290 assert(rixCmdFile != NULL);
291 StgThreadID tid = (current_tso == 0) ? 0 : current_tso->id;
293 // now check to see if we have met a breakpoint condition
294 if (rixCounter == rixBPCounter || (specialOpBP && tid != previousTid)) {
295 breakPointCommand(rixOp,tid);
299 if (tixBoxBP[rixOp] == 1) { // reached a bp tixbox
300 breakPointCommand(rixOp,tid);
305 breakPointCommand(rixOp,tid);
309 // update the history information.
311 rixOpBack[rixCounter % WOP_SIZE] = rixOp;
312 rixTidBack[rixCounter % WOP_SIZE] = tid;
316 fprintf(stderr,"end: hs_hpc_tick\n");
321 printEvent(FILE *out,StgWord64 rixCounter,StgThreadID rixTid,HpcRixOp rixOp) {
324 printEvent(stderr,rixCounter,rixTid,rixOp);
327 fprintf(out,"%" PRIuWORD64 " %u ",rixCounter,(unsigned int)rixTid);
329 case RixThreadFinishedOp:
330 fprintf(out,"ThreadFinished\n");
332 fprintf(out,"Raise\n");
334 fprintf(out,"Finished\n");
336 fprintf(out,"%u\n",rixOp);
341 breakPointCommand(HpcRixOp rixOp, StgThreadID rixTid) {
343 unsigned int tmp = 0;
344 printEvent(rixFile,rixCounter,rixTid,rixOp);
346 /* From here, you can ask some basic questions.
348 * c<nat> set the (one) counter breakpoint
349 * s<nat> set the (many) tickbox breakpoint
350 * u<nat> unset the (many) tickbox breakpoint
355 * Note that you aways end up here on the first tick
356 * because the specialOpBP is equal 0.
358 int c = getc(rixCmdFile);
359 while(c != 10 && c != -1) {
361 case 'c': // c1234 -- set counter breakpoint at 1234
362 c = getc(rixCmdFile);
365 tmp64 = tmp64 * 10 + (c - '0');
366 c = getc(rixCmdFile);
369 fprintf(stderr,"setting countBP = %" PRIuWORD64 "\n",tmp64);
371 rixBPCounter = tmp64;
373 case 's': // s2323 -- set tick box breakpoint at 2323
374 c = getc(rixCmdFile);
377 tmp = tmp * 10 + (c - '0');
378 c = getc(rixCmdFile);
381 fprintf(stderr,"seting bp for tix %d\n",tmp);
385 case 'u': // u2323 -- unset tick box breakpoint at 2323
386 c = getc(rixCmdFile);
389 tmp = tmp * 10 + (c - '0');
390 c = getc(rixCmdFile);
393 fprintf(stderr,"unseting bp for tix %d\n",tmp);
397 case 'x': // x -- set special bp flag
399 fprintf(stderr,"seting specialOpBP = 1\n");
402 c = getc(rixCmdFile);
404 case 'o': // o -- clear special bp flag
406 fprintf(stderr,"seting specialOpBP = 0\n");
409 c = getc(rixCmdFile);
411 case 'h': // h -- history of the last few (WOP_SIZE) steps
412 if (rixCounter > WOP_SIZE) {
413 tmp64 = rixCounter - WOP_SIZE;
417 for(;tmp64 < rixCounter;tmp64++) {
418 printEvent(rixFile,tmp64,rixTidBack[tmp64 % WOP_SIZE],rixOpBack[tmp64 % WOP_SIZE]);
421 c = getc(rixCmdFile);
425 fprintf(stderr,"strange command from HPCRIX (%d)\n",c);
427 c = getc(rixCmdFile);
429 while (c != 10) { // the end of the line
430 c = getc(rixCmdFile); // to the end of the line
432 c = getc(rixCmdFile); // the first char on the next command
435 fprintf(stderr,"re entering program\n");
439 /* This is called after all the modules have registered their local tixboxes,
440 * and does a sanity check: are we good to go?
449 fprintf(stderr,"startupHpc\n");
452 if (hpc_inited == 0) {
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",
464 fprintf(stderr,"(perhaps remove %s ?)\n",tixFilename);
470 // HPCRIX contains the name of the file to send our dynamic runtime output to (a named pipe).
472 hpcRix = getenv("HPCRIX");
479 rixFile = fopen(hpcRix,"w");
482 fprintf(rixFile,"Starting %s\n",prog_name);
483 fprintf(rixFile,"[");
485 for(;tmpModule != 0;tmpModule = tmpModule->next) {
487 fprintf(rixFile,",");
491 fprintf(rixFile,"(\"%s\",%u)",
493 tmpModule->tickCount);
495 fprintf(stderr,"(tracer)%s: %u (offset=%u)\n",
497 tmpModule->tickCount,
498 tmpModule->tickOffset);
501 fprintf(rixFile,"]\n");
504 // Now we open the command channel.
505 hpcRixCmd = getenv("HPCRIXCMD");
506 assert(hpcRixCmd != NULL);
507 rixCmdFile = fopen(hpcRixCmd,"r");
508 assert(rixCmdFile != NULL);
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));
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.
528 fprintf(stderr,"exitHpc\n");
531 if (hpc_inited == 0) {
535 FILE *f = fopen(tixFilename,"w");
539 fprintf(f,"Tix %" PRIuWORD64 " [", magicTixNumber);
541 for(;tmpModule != 0;tmpModule = tmpModule->next) {
547 fprintf(f,"(\"%s\",%u)",
549 tmpModule->tickCount);
551 fprintf(stderr,"%s: %u (offset=%u)\n",
553 tmpModule->tickCount,
554 tmpModule->tickOffset);
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",
567 for(i = 0;i < tmpModule->tickCount;i++) {
574 if (tmpModule->tixArr) {
575 fprintf(f,"%" PRIuWORD64,tmpModule->tixArr[i]);
586 if (rixFile != NULL) {
587 hs_hpc_tick(RixFinishedOp,(StgThreadID)0);
590 if (rixCmdFile != NULL) {