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