Using RTS debug tracing support inside Hpc.c
[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 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 static void failure(char *msg) {
68 debugTrace(DEBUG_hpc,"hpc failure: %s\n",msg);
69 fprintf(stderr,"Hpc failure: %s\n",msg);
70 fprintf(stderr,"(perhaps remove .tix file?)\n");
71 exit(-1);
72 }
73
74 static int init_open(char *filename)
75 {
76 tixFile = fopen(filename,"r");
77 if (tixFile == 0) {
78 return 0;
79 }
80 tix_ch = getc(tixFile);
81 return 1;
82 }
83
84 static void expect(char c) {
85 if (tix_ch != c) {
86 failure("parse error when reading .tix file");
87 }
88 tix_ch = getc(tixFile);
89 }
90
91 static void ws(void) {
92 while (tix_ch == ' ') {
93 tix_ch = getc(tixFile);
94 }
95 }
96
97 static char *expectString(void) {
98 char tmp[256], *res;
99 int tmp_ix = 0;
100 expect('"');
101 while (tix_ch != '"') {
102 tmp[tmp_ix++] = tix_ch;
103 tix_ch = getc(tixFile);
104 }
105 tmp[tmp_ix++] = 0;
106 expect('"');
107 res = malloc(tmp_ix);
108 strcpy(res,tmp);
109 return res;
110 }
111
112 static StgWord64 expectWord64(void) {
113 StgWord64 tmp = 0;
114 while (isdigit(tix_ch)) {
115 tmp = tmp * 10 + (tix_ch -'0');
116 tix_ch = getc(tixFile);
117 }
118 return tmp;
119 }
120
121 static void hpc_init(void) {
122 int i;
123 Info *tmpModule;
124
125 if (hpc_inited != 0) {
126 return;
127 }
128 hpc_inited = 1;
129
130
131 tixFilename = (char *) malloc(strlen(prog_name) + 6);
132 sprintf(tixFilename, "%s.tix", prog_name);
133
134 if (init_open(tixFilename)) {
135 totalTixes = 0;
136
137 ws();
138 expect('T');
139 expect('i');
140 expect('x');
141 ws();
142 magicTixNumber = expectWord64();
143 ws();
144 expect('[');
145 ws();
146 while(tix_ch != ']') {
147 tmpModule = (Info *)calloc(1,sizeof(Info));
148 expect('(');
149 ws();
150 tmpModule -> modName = expectString();
151 ws();
152 expect(',');
153 ws();
154 tmpModule -> tickCount = (int)expectWord64();
155 ws();
156 expect(')');
157 ws();
158
159 tmpModule -> tickOffset = totalTixes;
160 totalTixes += tmpModule -> tickCount;
161
162 tmpModule -> tixArr = 0;
163
164 if (!modules) {
165 modules = tmpModule;
166 } else {
167 nextModule->next=tmpModule;
168 }
169 nextModule=tmpModule;
170
171 if (tix_ch == ',') {
172 expect(',');
173 ws();
174 }
175 }
176 expect(']');
177 ws();
178 tixBoxes = (StgWord64 *)calloc(totalTixes,sizeof(StgWord64));
179
180 expect('[');
181 for(i = 0;i < totalTixes;i++) {
182 if (i != 0) {
183 expect(',');
184 ws();
185 }
186 tixBoxes[i] = expectWord64();
187 ws();
188 }
189 expect(']');
190
191 fclose(tixFile);
192 } else {
193 // later, we will find a binary specific
194 magicTixNumber = (StgWord64)0;
195 }
196 }
197
198 /* Called on a per-module basis, at startup time, declaring where the tix boxes are stored in memory.
199 * This memory can be uninitized, because we will initialize it with either the contents
200 * of the tix file, or all zeros.
201 */
202
203 int
204 hs_hpc_module(char *modName,int modCount,StgWord64 *tixArr) {
205 Info *tmpModule, *lastModule;
206 int i;
207 int offset = 0;
208
209 debugTrace(DEBUG_hpc,"hs_hpc_module(%s,%d)",modName,modCount);
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 debugTrace(DEBUG_hpc,"end: hs_hpc_module");
253
254 return offset;
255 }
256
257 static void breakPointCommand(HpcRixOp rixOp, StgThreadID rixTid);
258
259 // Breakpointing
260 static StgThreadID previousTid = 0;
261 static StgWord64 rixBPCounter = 0; // The global event breakpoint counter
262 static int *tixBoxBP;
263 static HpcRixOp rixOpBack[WOP_SIZE]; // The actual op
264 static HpcRixOp rixTidBack[WOP_SIZE]; // Tid's before the op
265
266 void
267 hs_hpc_raise_event(StgTSO *current_tso) {
268 hs_hpc_tick(RixRaiseOp,current_tso);
269 }
270
271 void
272 hs_hpc_thread_finished_event(StgTSO *current_tso) {
273 hs_hpc_tick(RixThreadFinishedOp,current_tso);
274 }
275
276 /* Called on every tick, dynamically, sending to our
277 * external record of program execution.
278 */
279
280 void
281 hs_hpc_tick(int rixOp, StgTSO *current_tso) {
282
283 debugTrace(DEBUG_hpc,"hs_hpc_tick(%x)",rixOp);
284
285 if (rixFile == NULL) {
286 return;
287 }
288 assert(rixCmdFile != NULL);
289 StgThreadID tid = (current_tso == 0) ? 0 : current_tso->id;
290
291 // now check to see if we have met a breakpoint condition
292 if (rixCounter == rixBPCounter
293 || tid != previousTid) {
294 breakPointCommand(rixOp,tid);
295 } else {
296 if (rixOp >= 0) {
297 // Tix op
298 if (tixBoxBP[rixOp] == 1) { // reached a bp tixbox
299 breakPointCommand(rixOp,tid);
300 }
301 } else {
302 // record the special operation
303 breakPointCommand(rixOp,tid);
304 }
305 }
306 // update the history information.
307 previousTid = tid;
308 rixOpBack[rixCounter % WOP_SIZE] = rixOp;
309 rixTidBack[rixCounter % WOP_SIZE] = tid;
310 rixCounter++;
311
312 debugTrace(DEBUG_hpc, "end: hs_hpc_tick");
313 }
314
315 static void
316 printEvent(FILE *out,StgWord64 rixCounter,StgThreadID rixTid,HpcRixOp rixOp) {
317 char prefixMsg[128];
318 char suffixMsg[128];
319
320 sprintf(prefixMsg,
321 "Event %" PRIuWORD64 " %u ",
322 rixCounter,
323 (unsigned int)rixTid);
324
325 switch(rixOp) {
326 case RixThreadFinishedOp:
327 sprintf(suffixMsg,"ThreadFinished");
328 break;
329 case RixRaiseOp:
330 sprintf(suffixMsg,"Raise");
331 break;
332 case RixFinishedOp:
333 sprintf(suffixMsg,"Finished");
334 break;
335 default:
336 sprintf(suffixMsg,"%u",rixOp);
337 }
338
339 fprintf(out,"%s%s\n",prefixMsg,suffixMsg);
340 debugTrace(DEBUG_hpc,"sending %s%s",prefixMsg,suffixMsg);
341 }
342
343 static void
344 breakPointCommand(HpcRixOp rixOp, StgThreadID rixTid) {
345 StgWord64 tmp64 = 0;
346 unsigned int tmp = 0;
347
348 if (getpid() != debuggee_pid) {
349 // We are not the original process, to do not issue
350 // any events, and do not try to talk to the debugger.
351 return;
352 }
353
354 debugTrace(DEBUG_hpc,"breakPointCommand %d %x",rixOp,(unsigned int)rixTid);
355
356 printEvent(rixFile,rixCounter,rixTid,rixOp);
357 fflush(rixFile);
358
359 /* From here, you can ask some basic questions.
360 *
361 * c<nat> set the (one) counter breakpoint
362 * s<nat> set the (many) tickbox breakpoint
363 * u<nat> unset the (many) tickbox breakpoint
364 * h history
365
366 * Note that you aways end up here on the first tick
367 * because the rixBPCounter starts equal to 0.
368 */
369 int c = getc(rixCmdFile);
370 while(c != 10 && c != -1) {
371 switch(c) {
372 case 'c': // c1234 -- set counter breakpoint at 1234
373 c = getc(rixCmdFile);
374 tmp64 = 0;
375 while(isdigit(c)) {
376 tmp64 = tmp64 * 10 + (c - '0');
377 c = getc(rixCmdFile);
378 }
379 debugTrace(DEBUG_hpc,"setting countBP = %" PRIuWORD64,tmp64);
380
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
391 debugTrace(DEBUG_hpc,"seting bp for tix %d",tmp);
392
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
403 debugTrace(DEBUG_hpc,"unseting bp for tix %d",tmp);
404
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,
415 tmp64,
416 rixTidBack[tmp64 % WOP_SIZE],
417 rixOpBack[tmp64 % WOP_SIZE]);
418 }
419 fflush(rixFile);
420 c = getc(rixCmdFile);
421 break;
422 default:
423
424 debugTrace(DEBUG_hpc,"strange command from HPCRIX (%d)",c);
425
426 c = getc(rixCmdFile);
427 }
428 while (c != 10) { // the end of the line
429 c = getc(rixCmdFile); // to the end of the line
430 }
431 c = getc(rixCmdFile); // the first char on the next command
432 }
433
434 debugTrace(DEBUG_hpc,"leaving breakPointCommand");
435
436 }
437
438 /* This is called after all the modules have registered their local tixboxes,
439 * and does a sanity check: are we good to go?
440 */
441
442 void
443 startupHpc(void) {
444 Info *tmpModule;
445 char *hpcRix;
446
447 debugTrace(DEBUG_hpc,"startupHpc");
448
449 if (hpc_inited == 0) {
450 return;
451 }
452
453 tmpModule = modules;
454
455 if (tixBoxes) {
456 for(;tmpModule != 0;tmpModule = tmpModule->next) {
457 totalTickCount += tmpModule->tickCount;
458 if (!tmpModule->tixArr) {
459 fprintf(stderr,"error: module %s did not register any hpc tick data\n",
460 tmpModule->modName);
461 fprintf(stderr,"(perhaps remove %s ?)\n",tixFilename);
462 exit(-1);
463 }
464 }
465 }
466
467 // HPCRIX contains the name of the file to send our dynamic runtime output to (a named pipe).
468
469 hpcRix = getenv("HPCRIX");
470 if (hpcRix) {
471 int comma;
472 Info *tmpModule;
473 int rixFD, rixCmdFD;
474
475 assert(hpc_inited);
476
477 if (sscanf(hpcRix,"%d:%d",&rixFD,&rixCmdFD) != 2) {
478 /* Bad format for HPCRIX.
479 */
480 debugTrace(DEBUG_hpc,"Bad HPCRIX (%s)",hpcRix);
481 exit(0);
482 }
483
484 debugTrace(DEBUG_hpc,"found HPCRIX pipes: %d:%d",rixFD,rixCmdFD);
485
486 rixFile = fdopen(rixFD,"w");
487 assert(rixFile != NULL);
488
489 rixCmdFile = fdopen(rixCmdFD,"r");
490 assert(rixCmdFile != NULL);
491
492 // If we fork a process, then we do not want ticks inside
493 // the sub-process to talk to the debugger. So we remember
494 // our pid at startup time, so we can check if we are still
495 // the original process.
496
497 debuggee_pid = getpid();
498
499 comma = 0;
500
501 fprintf(rixFile,"Starting %s\n",prog_name);
502 fprintf(rixFile,"[");
503 tmpModule = modules;
504 for(;tmpModule != 0;tmpModule = tmpModule->next) {
505 if (comma) {
506 fprintf(rixFile,",");
507 } else {
508 comma = 1;
509 }
510 fprintf(rixFile,"(\"%s\",%u)",
511 tmpModule->modName,
512 tmpModule->tickCount);
513
514 debugTrace(DEBUG_hpc,"(tracer)%s: %u (offset=%u)\n",
515 tmpModule->modName,
516 tmpModule->tickCount,
517 tmpModule->tickOffset);
518
519 }
520 fprintf(rixFile,"]\n");
521 fflush(rixFile);
522
523 // Allocate the tixBox breakpoint array
524 // These are set to 1 if you want to
525 // stop at a specific breakpoint
526 tixBoxBP = (int *)calloc(1,sizeof(int));
527 }
528
529 }
530
531
532 /* Called at the end of execution, to write out the Hpc *.tix file
533 * for this exection. Safe to call, even if coverage is not used.
534 */
535 void
536 exitHpc(void) {
537 Info *tmpModule;
538 int i, comma;
539
540 debugTrace(DEBUG_hpc,"exitHpc");
541
542 if (hpc_inited == 0) {
543 return;
544 }
545
546 FILE *f = fopen(tixFilename,"w");
547
548 comma = 0;
549
550 fprintf(f,"Tix %" PRIuWORD64 " [", magicTixNumber);
551 tmpModule = modules;
552 for(;tmpModule != 0;tmpModule = tmpModule->next) {
553 if (comma) {
554 fprintf(f,",");
555 } else {
556 comma = 1;
557 }
558 fprintf(f,"(\"%s\",%u)",
559 tmpModule->modName,
560 tmpModule->tickCount);
561 debugTrace(DEBUG_hpc,"%s: %u (offset=%u)\n",
562 tmpModule->modName,
563 tmpModule->tickCount,
564 tmpModule->tickOffset);
565 }
566 fprintf(f,"] [");
567
568 comma = 0;
569 tmpModule = modules;
570 for(;tmpModule != 0;tmpModule = tmpModule->next) {
571 if (!tmpModule->tixArr) {
572 debugTrace(DEBUG_hpc,
573 "warning: module %s did not register any hpc tick data\n",
574 tmpModule->modName);
575 }
576
577 for(i = 0;i < tmpModule->tickCount;i++) {
578 if (comma) {
579 fprintf(f,",");
580 } else {
581 comma = 1;
582 }
583
584 if (tmpModule->tixArr) {
585 fprintf(f,"%" PRIuWORD64,tmpModule->tixArr[i]);
586 } else {
587 fprintf(f,"0");
588 }
589
590 }
591 }
592
593 fprintf(f,"]\n");
594 fclose(f);
595
596 if (rixFile != NULL) {
597 hs_hpc_tick(RixFinishedOp,(StgThreadID)0);
598 fclose(rixFile);
599 }
600 if (rixCmdFile != NULL) {
601 fclose(rixCmdFile);
602 }
603
604 }
605