Changing internal data structures used by Hpc
[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
11 #include "Rts.h"
12 #include "Hpc.h"
13 #include "Trace.h"
14
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18
19
20 /* This is the runtime support for the Haskell Program Coverage (hpc) toolkit,
21 * inside GHC.
22 *
23 */
24
25 #define WOP_SIZE 1024
26
27 static int hpc_inited = 0; // Have you started this component?
28 static FILE *tixFile; // file being read/written
29 static int tix_ch; // current char
30
31 static FILE *rixFile = NULL; // The tracer file/pipe (to debugger)
32 static FILE *rixCmdFile = NULL; // The tracer file/pipe (from debugger)
33 static StgWord64 rixCounter = 0; // The global event counter
34 static int debuggee_pid;
35
36 typedef enum {
37 RixThreadFinishedOp = -1,
38 RixRaiseOp = -2,
39 RixFinishedOp = -3
40 } HpcRixOp;
41
42
43 typedef struct _Info {
44 char *modName; // name of module
45 int tickCount; // number of ticks
46 int tickOffset; // offset into a single large .tix Array
47 int hashNo; // Hash number for this module's mix info
48 StgWord64 *tixArr; // tix Array from the program execution (local for this module)
49 struct _Info *next;
50 } Info;
51
52 // This is a cruel hack, we should completely redesign the format specifier handling in the RTS.
53 #if SIZEOF_LONG == 8
54 #define PRIuWORD64 "lu"
55 #else
56 #define PRIuWORD64 "llu"
57 #endif
58
59 Info *modules = 0;
60 Info *nextModule = 0;
61 int totalTixes = 0; // total number of tix boxes.
62
63 static char *tixFilename;
64
65 static void failure(char *msg) {
66 debugTrace(DEBUG_hpc,"hpc failure: %s\n",msg);
67 fprintf(stderr,"Hpc failure: %s\n",msg);
68 if (tixFilename) {
69 fprintf(stderr,"(perhaps remove %s file?)\n",tixFilename);
70 } else {
71 fprintf(stderr,"(perhaps remove .tix file?)\n");
72 }
73 exit(-1);
74 }
75
76 static int init_open(char *filename)
77 {
78 tixFile = fopen(filename,"r");
79 if (tixFile == 0) {
80 return 0;
81 }
82 tix_ch = getc(tixFile);
83 return 1;
84 }
85
86 static void expect(char c) {
87 if (tix_ch != c) {
88 fprintf(stderr,"('%c' '%c')\n",tix_ch,c);
89 failure("parse error when reading .tix file");
90 }
91 tix_ch = getc(tixFile);
92 }
93
94 static void ws(void) {
95 while (tix_ch == ' ') {
96 tix_ch = getc(tixFile);
97 }
98 }
99
100 static char *expectString(void) {
101 char tmp[256], *res;
102 int tmp_ix = 0;
103 expect('"');
104 while (tix_ch != '"') {
105 tmp[tmp_ix++] = tix_ch;
106 tix_ch = getc(tixFile);
107 }
108 tmp[tmp_ix++] = 0;
109 expect('"');
110 res = malloc(tmp_ix);
111 strcpy(res,tmp);
112 return res;
113 }
114
115 static StgWord64 expectWord64(void) {
116 StgWord64 tmp = 0;
117 while (isdigit(tix_ch)) {
118 tmp = tmp * 10 + (tix_ch -'0');
119 tix_ch = getc(tixFile);
120 }
121 return tmp;
122 }
123
124 static void hpc_init(void) {
125 int i;
126 Info *tmpModule;
127
128 if (hpc_inited != 0) {
129 return;
130 }
131 hpc_inited = 1;
132
133 tixFilename = (char *) malloc(strlen(prog_name) + 6);
134 sprintf(tixFilename, "%s.tix", prog_name);
135
136 if (init_open(tixFilename)) {
137 totalTixes = 0;
138
139 ws();
140 expect('T');
141 expect('i');
142 expect('x');
143 ws();
144 expect('[');
145 ws();
146 while(tix_ch != ']') {
147 tmpModule = (Info *)calloc(1,sizeof(Info));
148 expect('T');
149 expect('i');
150 expect('x');
151 expect('M');
152 expect('o');
153 expect('d');
154 expect('u');
155 expect('l');
156 expect('e');
157 ws();
158 tmpModule -> modName = expectString();
159 ws();
160 tmpModule -> hashNo = (unsigned int)expectWord64();
161 ws();
162 tmpModule -> tickCount = (int)expectWord64();
163 tmpModule -> tixArr = (StgWord64 *)calloc(tmpModule->tickCount,sizeof(StgWord64));
164 tmpModule -> tickOffset = totalTixes;
165 totalTixes += tmpModule -> tickCount;
166 ws();
167 expect('[');
168 ws();
169 for(i = 0;i < tmpModule->tickCount;i++) {
170 tmpModule->tixArr[i] = expectWord64();
171 ws();
172 if (tix_ch == ',') {
173 expect(',');
174 ws();
175 }
176 }
177 expect(']');
178 ws();
179
180 if (!modules) {
181 modules = tmpModule;
182 } else {
183 nextModule->next=tmpModule;
184 }
185 nextModule=tmpModule;
186
187 if (tix_ch == ',') {
188 expect(',');
189 ws();
190 }
191 }
192 expect(']');
193 fclose(tixFile);
194 }
195 }
196
197 /* Called on a per-module basis, at startup time, declaring where the tix boxes are stored in memory.
198 * This memory can be uninitized, because we will initialize it with either the contents
199 * of the tix file, or all zeros.
200 */
201
202 int
203 hs_hpc_module(char *modName,
204 int modCount,
205 int modHashNo,
206 StgWord64 *tixArr) {
207 Info *tmpModule, *lastModule;
208 int i;
209 int offset = 0;
210
211 debugTrace(DEBUG_hpc,"hs_hpc_module(%s,%d)",modName,modCount);
212
213 hpc_init();
214
215 tmpModule = modules;
216 lastModule = 0;
217
218 for(;tmpModule != 0;tmpModule = tmpModule->next) {
219 if (!strcmp(tmpModule->modName,modName)) {
220 if (tmpModule->tickCount != modCount) {
221 failure("inconsistent number of tick boxes");
222 }
223 assert(tmpModule->tixArr != 0);
224 if (tmpModule->hashNo != modHashNo) {
225 fprintf(stderr,"in module '%s'\n",tmpModule->modName);
226 failure("module mismatch with .tix/.mix file hash number");
227 fprintf(stderr,"(perhaps remove %s ?)\n",tixFilename);
228 exit(-1);
229
230 }
231 for(i=0;i < modCount;i++) {
232 tixArr[i] = tmpModule->tixArr[i];
233 }
234 tmpModule->tixArr = tixArr;
235 return tmpModule->tickOffset;
236 }
237 lastModule = tmpModule;
238 }
239 // Did not find entry so add one on.
240 tmpModule = (Info *)calloc(1,sizeof(Info));
241 tmpModule->modName = modName;
242 tmpModule->tickCount = modCount;
243 tmpModule->hashNo = modHashNo;
244 if (lastModule) {
245 tmpModule->tickOffset = lastModule->tickOffset + lastModule->tickCount;
246 } else {
247 tmpModule->tickOffset = 0;
248 }
249 tmpModule->tixArr = tixArr;
250 for(i=0;i < modCount;i++) {
251 tixArr[i] = 0;
252 }
253 tmpModule->next = 0;
254
255 if (!modules) {
256 modules = tmpModule;
257 } else {
258 lastModule->next=tmpModule;
259 }
260
261 debugTrace(DEBUG_hpc,"end: hs_hpc_module");
262
263 return offset;
264 }
265
266 static void breakPointCommand(HpcRixOp rixOp, StgThreadID rixTid);
267
268 // Breakpointing
269 static StgThreadID previousTid = 0;
270 static StgWord64 rixBPCounter = 0; // The global event breakpoint counter
271 static int *tixBoxBP;
272 static HpcRixOp rixOpBack[WOP_SIZE]; // The actual op
273 static HpcRixOp rixTidBack[WOP_SIZE]; // Tid's before the op
274
275 void
276 hs_hpc_raise_event(StgTSO *current_tso) {
277 hs_hpc_tick(RixRaiseOp,current_tso);
278 }
279
280 void
281 hs_hpc_thread_finished_event(StgTSO *current_tso) {
282 hs_hpc_tick(RixThreadFinishedOp,current_tso);
283 }
284
285 /* Called on every tick, dynamically, sending to our
286 * external record of program execution.
287 */
288
289 void
290 hs_hpc_tick(int rixOp, StgTSO *current_tso) {
291
292 debugTrace(DEBUG_hpc,"hs_hpc_tick(%x)",rixOp);
293
294 if (rixFile == NULL) {
295 return;
296 }
297 assert(rixCmdFile != NULL);
298 StgThreadID tid = (current_tso == 0) ? 0 : current_tso->id;
299
300 // now check to see if we have met a breakpoint condition
301 if (rixCounter == rixBPCounter
302 || tid != previousTid) {
303 breakPointCommand(rixOp,tid);
304 } else {
305 if (rixOp >= 0) {
306 // Tix op
307 if (tixBoxBP[rixOp] == 1) { // reached a bp tixbox
308 breakPointCommand(rixOp,tid);
309 }
310 } else {
311 // record the special operation
312 breakPointCommand(rixOp,tid);
313 }
314 }
315 // update the history information.
316 previousTid = tid;
317 rixOpBack[rixCounter % WOP_SIZE] = rixOp;
318 rixTidBack[rixCounter % WOP_SIZE] = tid;
319 rixCounter++;
320
321 debugTrace(DEBUG_hpc, "end: hs_hpc_tick");
322 }
323
324 static void
325 printEvent(FILE *out,StgWord64 rixCounter,StgThreadID rixTid,HpcRixOp rixOp) {
326 char prefixMsg[128];
327 char suffixMsg[128];
328
329 sprintf(prefixMsg,
330 "Event %" PRIuWORD64 " %u ",
331 rixCounter,
332 (unsigned int)rixTid);
333
334 switch(rixOp) {
335 case RixThreadFinishedOp:
336 sprintf(suffixMsg,"ThreadFinished");
337 break;
338 case RixRaiseOp:
339 sprintf(suffixMsg,"Raise");
340 break;
341 case RixFinishedOp:
342 sprintf(suffixMsg,"Finished");
343 break;
344 default:
345 sprintf(suffixMsg,"%u",rixOp);
346 }
347
348 fprintf(out,"%s%s\n",prefixMsg,suffixMsg);
349 debugTrace(DEBUG_hpc,"sending %s%s",prefixMsg,suffixMsg);
350 }
351
352 static void
353 breakPointCommand(HpcRixOp rixOp, StgThreadID rixTid) {
354 StgWord64 tmp64 = 0;
355 unsigned int tmp = 0;
356
357 if (getpid() != debuggee_pid) {
358 // We are not the original process, to do not issue
359 // any events, and do not try to talk to the debugger.
360 return;
361 }
362
363 debugTrace(DEBUG_hpc,"breakPointCommand %d %x",rixOp,(unsigned int)rixTid);
364
365 printEvent(rixFile,rixCounter,rixTid,rixOp);
366 fflush(rixFile);
367
368 /* From here, you can ask some basic questions.
369 *
370 * c<nat> set the (one) counter breakpoint
371 * s<nat> set the (many) tickbox breakpoint
372 * u<nat> unset the (many) tickbox breakpoint
373 * h history
374
375 * Note that you aways end up here on the first tick
376 * because the rixBPCounter starts equal to 0.
377 */
378 int c = getc(rixCmdFile);
379 while(c != 10 && c != -1) {
380 switch(c) {
381 case 'c': // c1234 -- set counter breakpoint at 1234
382 c = getc(rixCmdFile);
383 tmp64 = 0;
384 while(isdigit(c)) {
385 tmp64 = tmp64 * 10 + (c - '0');
386 c = getc(rixCmdFile);
387 }
388 debugTrace(DEBUG_hpc,"setting countBP = %" PRIuWORD64,tmp64);
389
390 rixBPCounter = tmp64;
391 break;
392 case 's': // s2323 -- set tick box breakpoint at 2323
393 c = getc(rixCmdFile);
394 tmp = 0;
395 while(isdigit(c)) {
396 tmp = tmp * 10 + (c - '0');
397 c = getc(rixCmdFile);
398 }
399
400 debugTrace(DEBUG_hpc,"seting bp for tix %d",tmp);
401
402 tixBoxBP[tmp] = 1;
403 break;
404 case 'u': // u2323 -- unset tick box breakpoint at 2323
405 c = getc(rixCmdFile);
406 tmp = 0;
407 while(isdigit(c)) {
408 tmp = tmp * 10 + (c - '0');
409 c = getc(rixCmdFile);
410 }
411
412 debugTrace(DEBUG_hpc,"unseting bp for tix %d",tmp);
413
414 tixBoxBP[tmp] = 0;
415 break;
416 case 'h': // h -- history of the last few (WOP_SIZE) steps
417 if (rixCounter > WOP_SIZE) {
418 tmp64 = rixCounter - WOP_SIZE;
419 } else {
420 tmp64 = 0;
421 }
422 for(;tmp64 < rixCounter;tmp64++) {
423 printEvent(rixFile,
424 tmp64,
425 rixTidBack[tmp64 % WOP_SIZE],
426 rixOpBack[tmp64 % WOP_SIZE]);
427 }
428 fflush(rixFile);
429 c = getc(rixCmdFile);
430 break;
431 default:
432
433 debugTrace(DEBUG_hpc,"strange command from HPCRIX (%d)",c);
434
435 c = getc(rixCmdFile);
436 }
437 while (c != 10) { // the end of the line
438 c = getc(rixCmdFile); // to the end of the line
439 }
440 c = getc(rixCmdFile); // the first char on the next command
441 }
442
443 debugTrace(DEBUG_hpc,"leaving breakPointCommand");
444
445 }
446
447 /* This is called after all the modules have registered their local tixboxes,
448 * and does a sanity check: are we good to go?
449 */
450
451 void
452 startupHpc(void) {
453 char *hpcRix;
454
455 debugTrace(DEBUG_hpc,"startupHpc");
456
457 if (hpc_inited == 0) {
458 return;
459 }
460 // HPCRIX contains the name of the file to send our dynamic runtime output to (a named pipe).
461
462 hpcRix = getenv("HPCRIX");
463 if (hpcRix) {
464 int comma;
465 Info *tmpModule;
466 int rixFD, rixCmdFD;
467 int tixCount = 0;
468
469 assert(hpc_inited);
470
471 if (sscanf(hpcRix,"%d:%d",&rixFD,&rixCmdFD) != 2) {
472 /* Bad format for HPCRIX.
473 */
474 debugTrace(DEBUG_hpc,"Bad HPCRIX (%s)",hpcRix);
475 exit(0);
476 }
477
478 debugTrace(DEBUG_hpc,"found HPCRIX pipes: %d:%d",rixFD,rixCmdFD);
479
480 rixFile = fdopen(rixFD,"w");
481 assert(rixFile != NULL);
482
483 rixCmdFile = fdopen(rixCmdFD,"r");
484 assert(rixCmdFile != NULL);
485
486 // If we fork a process, then we do not want ticks inside
487 // the sub-process to talk to the debugger. So we remember
488 // our pid at startup time, so we can check if we are still
489 // the original process.
490
491 debuggee_pid = getpid();
492
493 comma = 0;
494
495 fprintf(rixFile,"Starting %s\n",prog_name);
496 fprintf(rixFile,"[");
497 tmpModule = modules;
498 for(;tmpModule != 0;tmpModule = tmpModule->next) {
499 if (comma) {
500 fprintf(rixFile,",");
501 } else {
502 comma = 1;
503 }
504 fprintf(rixFile,"(\"%s\",%u)",
505 tmpModule->modName,
506 tmpModule->tickCount);
507
508 tixCount += tmpModule->tickCount;
509
510 debugTrace(DEBUG_hpc,"(tracer)%s: %u (offset=%u) (hash=%u)\n",
511 tmpModule->modName,
512 tmpModule->tickCount,
513 tmpModule->hashNo,
514 tmpModule->tickOffset);
515
516 }
517 fprintf(rixFile,"]\n");
518 fflush(rixFile);
519
520 // Allocate the tixBox breakpoint array
521 // These are set to 1 if you want to
522 // stop at a specific breakpoint
523 tixBoxBP = (int *)calloc(tixCount,sizeof(int));
524 }
525
526 }
527
528
529 /* Called at the end of execution, to write out the Hpc *.tix file
530 * for this exection. Safe to call, even if coverage is not used.
531 */
532 void
533 exitHpc(void) {
534 Info *tmpModule;
535 int i, inner_comma, outer_comma;
536
537 debugTrace(DEBUG_hpc,"exitHpc");
538
539 if (hpc_inited == 0) {
540 return;
541 }
542
543 FILE *f = fopen(tixFilename,"w");
544
545 outer_comma = 0;
546
547 fprintf(f,"Tix [");
548 tmpModule = modules;
549 for(;tmpModule != 0;tmpModule = tmpModule->next) {
550 if (outer_comma) {
551 fprintf(f,",");
552 } else {
553 outer_comma = 1;
554 }
555 fprintf(f," TixModule \"%s\" %u %u [",
556 tmpModule->modName,
557 tmpModule->hashNo,
558 tmpModule->tickCount);
559 debugTrace(DEBUG_hpc,"%s: %u (offset=%u) (hash=%u)\n",
560 tmpModule->modName,
561 tmpModule->tickCount,
562 tmpModule->hashNo,
563 tmpModule->tickOffset);
564
565 inner_comma = 0;
566 for(i = 0;i < tmpModule->tickCount;i++) {
567 if (inner_comma) {
568 fprintf(f,",");
569 } else {
570 inner_comma = 1;
571 }
572
573 if (tmpModule->tixArr) {
574 fprintf(f,"%" PRIuWORD64,tmpModule->tixArr[i]);
575 } else {
576 fprintf(f,"0");
577 }
578 }
579 fprintf(f,"]");
580 }
581 fprintf(f,"]\n");
582
583 /*
584 tmpModule = modules;
585 for(;tmpModule != 0;tmpModule = tmpModule->next) {
586 if (!tmpModule->tixArr) {
587 debugTrace(DEBUG_hpc,
588 "warning: module %s did not register any hpc tick data\n",
589 tmpModule->modName);
590 }
591
592 for(i = 0;i < tmpModule->tickCount;i++) {
593 if (comma) {
594 fprintf(f,",");
595 } else {
596 comma = 1;
597 }
598
599 if (tmpModule->tixArr) {
600 fprintf(f,"%" PRIuWORD64,tmpModule->tixArr[i]);
601 } else {
602 fprintf(f,"0");
603 }
604
605 }
606 }
607
608 fprintf(f,"]\n");
609 */
610 fclose(f);
611
612 if (rixFile != NULL) {
613 hs_hpc_tick(RixFinishedOp,(StgThreadID)0);
614 fclose(rixFile);
615 }
616 if (rixCmdFile != NULL) {
617 fclose(rixCmdFile);
618 }
619
620 }
621